GIS & Spatial Data
GIS stands for Geographic Information System/Science - the System part refers to the software used for capturing, storing, and manipulating spatial data, while the Science definition is concerned with scientific principles behind spatial analysis, and developing new methods and approaches to extract insight from geographic data.
Spatial Data - any data set which has, or has the potential to have, location attached to it. This includes, but is not limited to, coordinates, addresses, and geography codes.
Spatial data types:
- Vector - points, lines, and polygons used to represent physical and administrative features. Used for displaying data with well defined extent.
- Raster - pixel based data, often derived from satellite imagery. Used for displaying continuous or fuzzy variables.
Common spatial data formats:
- Shapefile (.shp)
- GeoPackage (.gpkg) / Geodatabase (.gdb)
- GeoJSON / TopoJSON
- Well-known-text
- GeoTiff
Map Projection and Coordinate Reference System
In cartography, a map projection is a way to flatten a globe’s surface into a plane in order to make a map. This requires a systematic transformation of the latitudes and longitudes of locations from the surface of the globe into locations on a plane. All projections of a sphere on a plane necessarily distort the surface in some way and to some extent. Depending on the purpose of the map, some distortions are acceptable and others are not; therefore, different map projections exist in order to preserve some properties of the sphere-like body at the expense of other properties. Every distinct map projection distorts in a distinct way, by definition.
Source: Wikipedia
Source
All you need to know for today is:
- When working with GB data use the British National Grid (BNG).
- BNG uses Eastings and Northings which are given as metres, offset from the origin point.
- BNG’s EPSG code is 27700.

GIS and R
R is commonly used for statistical analysis and programming, however it also has a whole range of GIS tools. There is a long history of geospatial libraries being developed for R and an amazing community of researchers and programmers around it. In the last few years, working with spatial data became much easier in R, with the development of the sf package. sf keeps all spatial information for each observation in a geometry column which means that we can treat it like a normal data frame but also perform all types of spatial operations on the data.
Simple feature collection with 6 features and 2 fields
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: 543417.3 ymin: 183488.5 xmax: 551943.8 ymax: 191137.3
epsg (SRID): NA
proj4string: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +datum=OSGB36 +units=m +no_defs
wd19cd wd19nm geometry
1 E05000026 Abbey MULTIPOLYGON (((544338.3 18...
2 E05000027 Alibon MULTIPOLYGON (((549604.1 18...
3 E05000028 Becontree MULTIPOLYGON (((547563.4 18...
4 E05000029 Chadwell Heath MULTIPOLYGON (((548881 1910...
5 E05000030 Eastbrook MULTIPOLYGON (((551552.9 18...
6 E05000031 Eastbury MULTIPOLYGON (((547271.2 18...
Sources of spatial data
When working with most types of data you will most commonly need to link them to official administrative and census boundaries, as well as more general geographic data. Most national and local governments will have a geography portal where boundaries can be downloaded. At ONS boundaries, lookups, and documentation can be downloaded from the Open Geography Portal, while other geographic featues can be accessed from the Ordnance Survey.
Boundaries managed by the ONS come in several resolutions (more generalised boundaries sacrifice accuracy and precision for file size and processing speeds):
- F - Full resolution
- G - Generalised
- U - Ultra generalised
and extents (extent determines where the land/water boundary is - extent of the realm includes areas of water):
- C - Clipped to coastline
- E - Extent of the realm
Exercise - Open Geography Portal
- Go to the Open Geography Portal and download the 2019 Local Authority District BGC boundaries. Save them to
data/shp in your project structure and unzip.
- Open the folder and see how many different files there are.
London Fire Brigade Animal Rescue Data
Troughout this tutorial we will be using data from the London Fire Brigade - LFB Animal Rescue Data. It covers all incidents between 2009 and 2020 which included assistance to animals that may be trapped or in distress. The data is updated monthly and includes a range of variables for each incident including some location information (postcode, borough, ward),the date/time of the incidents, cost, and type of animal in trouble.
We want to visualise, and better understand how much money has been spent on animal related incidents between 2009 and 2020, and what the distribution is at the MSOA level of geography. To achieve this we will have to import spatial data, manipulate it and create summary statistics, and then plot it.
Loading spatial and non-spatial data
LFB data has been tidied up and saved as a Comma Separated Value file (.csv). We can use read_csv to open it in R.
Exercise - open LFB data
- Create a new object called
lfb by using read_csv(). Load data located in “data/csv/lfb_2009_2020.csv”.
- Use
glimpse() or head() to view lfb structure.
Solution
lfb <- read_csv("data/csv/lfb_2009_2020.csv")
head(lfb)
lfb is currently just a data frame. It has not got an explicit geometry column which links observations to their geographic location. It does however contain several columns which can be used to convert it into a spatial data format.
Ward_code column references the GSS codes of wards within which the observations fall. GSS codes can be used to join lfb data to boundaries from the Open Geography Portal. One issue with this particular column is that it does not indicate the currency of GSS codes. Wards are subject to frequent change, and as such it is best practice to be clear about the dates of any boundaries used by stating the exact code used, e.g. wd19cd. Because LFB data does not include this information we have no guarantee that the boundaries and GSS codes we join will match.
Fortunately we have also been provided with columns recording the easting, and northing of each incident. We can use those to convert lfb into an sf object. To achieve this we will use the st_as_sf() function which takes the following arguments:
new_object <- st_as_sf(x = input_data_frame, coords = c("x_coordinate_column", "y_coordinate_column"), crs = 27700)
Exercise - create spatial data
- Create a new object called
lfb_sf by converting lfb using the st_as_sf() function.
- Use
glimpse() or head() to view lfb_sf structure.
Solution
lfb_sf <- st_as_sf(x = lfb, coords = c("easting", "northing"), crs = 27700)
head(lfb_sf)
Simple feature collection with 6 features and 10 fields
geometry type: POINT
dimension: XY
bbox: xmin: 504650 ymin: 164950 xmax: 554650 ymax: 192350
epsg (SRID): 27700
proj4string: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs
st_as_sf() converted the easting and northing columns to simple feature geometries and created a new column called geometry which holds spatial information for each row. Now that lfb is a spatial object we can plot it using the tmap package. For now we will use the qtm() function which creates a quick map, using tmap's default settings. qtm() only needs to be supplied with a simple feature object and is very useful for quickly inspecting your data.
Exercise - quick static maps
- Plot lfb_sf using the
qtm() function.
Solution
qtm(lfb_sf)

We can also create interactive maps using the tmap, package by running tmap_mode("view") before executing qtm(). To reverse it and go back to static maps use tmap_mode("plot").
Exercise - quick interactive maps
- Make an interactive map of
lfb_sf using the qtm() function and tmap_mode("view").
Solution
tmap_mode("view")
qtm(lfb_sf)
Filtering by GSS code
It looks like some of the locations are located outside of London, however we are only interested in incidents within the Local Authority Districts making up Greater London. To remove all points outside of London we will have to first import the LAD boundaries which we downloaded from the Open Geography Portal and then use them to spatially filter lfb_sf data.
So far we have created our own sf objects by adding a geometry column. The LAD data set is already a spatial one and as such we can use the st_read() function from the sf package to import it. st_read is extremely versatile and able to import most spatial data formats into R. The only argument that needs to be supplied to st_read is the full path to the LAD boundaries
Exercise - loading shapefiles
- Use
st_read() to load the LAD boundaries you downloaded at the beginning of the tutorial, aslad_2019.
- LAD path -
data/shp/Local_Authority_Districts_December_2019_Boundaries_UK_BGC/ Local_Authority_Districts_December_2019_Boundaries_UK_BGC.shp
- Make a static map of the object you have just created using
qtm() and setting tmap_mode("plot").
Solution
lad_2019 <- st_read("data/shp/Local_Authority_Districts_December_2019_Boundaries_UK_BGC/Local_Authority_Districts_December_2019_Boundaries_UK_BGC.shp")
Reading layer `Local_Authority_Districts_December_2019_Boundaries_UK_BGC' from data source `D:\1_projects\Intro_to_geography\intro_to_gis\data\shp\Local_Authority_Districts_December_2019_Boundaries_UK_BGC\Local_Authority_Districts_December_2019_Boundaries_UK_BGC.shp' using driver `ESRI Shapefile'
Simple feature collection with 382 features and 10 fields
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: -116.1928 ymin: 5342.7 xmax: 655653.8 ymax: 1220302
epsg (SRID): NA
proj4string: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +datum=OSGB36 +units=m +no_defs
tmap_mode("plot")
tmap mode set to plotting
qtm(lad_2019)

LAD boundaries have loaded correctly but they currently cover all of the UK when all we need is London. Because simple feature objects are data frames with a geometry column attached, any operations that we would perform on a normal data frame can also be performed on an object of class sf. Here we will use the dplyr::filter and stringr::str_detect() from the the tidyverse package to only keep LADs whose GSS code starts with “E09”.
Exercise - filter spatial data by variable
- Inspect lad_2019 using
head() or glimpse(), and identify which column holds the GSS codes - it should end in “cd”.
- Create a new object called
london_lad. Use dplyr::filter alongside stringr::str_detect() to only keep observations which have a GSS code starting with “E09”.
- Plot
london_lad to see if the results look correct.
Solution
head(lad_2019)
Simple feature collection with 6 features and 10 fields
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: 344666.1 ymin: 378867 xmax: 478441.5 ymax: 537152
epsg (SRID): NA
proj4string: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +datum=OSGB36 +units=m +no_defs
objectid lad19cd lad19nm lad19nmw bng_e bng_n long lat st_areasha st_lengths geometry
1 1 E06000001 Hartlepool <NA> 447160 531474 -1.270189 54.67614 93770350 68481.65 MULTIPOLYGON (((447097 5371...
2 2 E06000002 Middlesbrough <NA> 451141 516887 -1.210998 54.54468 53858124 42570.87 MULTIPOLYGON (((449862.8 52...
3 3 E06000003 Redcar and Cleveland <NA> 464361 519597 -1.006086 54.56752 245140395 94686.62 MULTIPOLYGON (((455939.7 52...
4 4 E06000004 Stockton-on-Tees <NA> 444940 518183 -1.306645 54.55691 204903681 118320.90 MULTIPOLYGON (((444126.1 52...
5 5 E06000005 Darlington <NA> 428029 515648 -1.568356 54.53534 197485809 105777.87 MULTIPOLYGON (((423475.7 52...
6 6 E06000006 Halton <NA> 354246 382146 -2.688538 53.33425 79096448 76349.03 MULTIPOLYGON (((358374.7 38...
london_lad <- filter(lad_2019, str_detect(lad19cd, "E09"))
qtm(london_lad)

Finally, for the next step, we only need the outer boundary of London - all the internal LAD boundaries have to be removed and only the outer edges kept. sf has a function exactly for this purpose called st_union(). It only takes one argument, which is the sf object we want to unionise.
Exercise - dissolve boundaries
- Create a new object called
london_boundary using the st_union function.
- Plot it to check the results.
Solution
london_boundary <- st_union(london_lad)
qtm(london_boundary)

Spatial subsetting and CRS
In addition to subsetting by value, as we did with the LAD boundaries earlier, we can also subset observations by evaluating their spatial relationship with another data set. We can for example select all LADs which are fully within Wales, every Output Area intersected by a river, or all households outside of city boundaries. There are a number of different spatial relationships which can be tested and used to subset observations.
sf has an inbuilt function called st_filter() which we can use to spatially subset observations. The function takes several arguments:
- x -
sf data frame we want to subset - lfb_sf
- y -
sf object used to evaluate the spatial relationship - london_boundary
Before running any spatial operations on two spatial objects it is always worth checking if their coordinate reference systems (CRS) match. sf will throw an error if that’s not the case. Try it for yourself below.
Exercise - spatial subset part 1
- Use
st_filter() to spatially subset lfb_sf by testing its relationship with london_boundary.
Solution:
lfb_sf <- st_filter(x = lfb_sf, y = london_boundary)
You should have got an error here saying Error in !inherits(x, "sf") : st_crs(x) == st_crs(y) is not TRUE. It means that objects x and y have different CRS. We can see this for ourselves by running the st_crs() function, which returns the coordinate reference system of an object.
Exercise - check CRS
- Run
st_crs() on both and lfb_sf and london_boundary and compare the results.
Solution:
st_crs(lfb_sf)
Coordinate Reference System:
EPSG: 27700
proj4string: "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs"
st_crs(london_boundary)
Coordinate Reference System:
No EPSG code
proj4string: "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +datum=OSGB36 +units=m +no_defs"
We can see that london_boundary has not got an EPSG code, and that the proj4string information is different. We can solve this problem by transforming london_boundary’s CRS to match that of lfb_sf, simply by using the correct EPSG code. To do so we will use the st_transform() function which takes two arguments:
- x -
sf object to be transformed
- crs - EPSG code that we want to transform our data to - BNG is 27700.
Exercise - spatial subset part 2
- Use
st_filter to spatially subset lfb_sf by testing its relationship with london_boundary. Overwrite lfb_sf with the subset data.
- Plot it to check if the results are correct.
Solution:
lfb_sf <- st_filter(x = lfb_sf, y = london_boundary)
qtm(lfb_sf)

Spatial and non-spatial joins
Simple features data can be joined to other data sets in two ways. We can either use a traditional, SQL like join, based on a value shared across the data sets or, since we have a geometry column, on the spatial relationship between the data sets. This is known as a spatial join, where variables from one data set are joined to another one only on the basis of their spatial relationship. The most commonly used operation is known as a Point-in-Polygon join where data from a polygon is joined to the points within them.
In sf spatial joins are handled using the st_join() function with arguments:
- x -
sf object to which we are joining data (LHS in SQL)
- y -
sf object whose variables are being joined (RHS in SQL)
We will be joining the Middle Super Output Areas to LFB locations, which will then allow us to group and plot data at MSOA level.
Exercise - spatial joins
- Read in
data/shp/MSOA_2011_london/msoa_2011_ew_bgc.shp as msoa_london - use st_read()
- Check if
msoa_london’s CRS and that of lfb_sf match. Transform msoa_london if necessary.
- Create a new object called
lfb_msoa_sf by running st_join() between lfb_sf and msoa_london
- Inspect your new object using
head() or glimpse() to see what columns have been added.
msoa_london <- st_read("data/shp/MSOA_2011_london/msoa_2011_ew_bgc.shp")
Reading layer `msoa_2011_ew_bgc' from data source `D:\1_projects\Intro_to_geography\intro_to_gis\data\shp\MSOA_2011_london\msoa_2011_ew_bgc.shp' using driver `ESRI Shapefile'
Simple feature collection with 983 features and 2 fields
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: 503574.2 ymin: 155850.8 xmax: 561956.7 ymax: 200933.6
epsg (SRID): NA
proj4string: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +units=m +no_defs
st_crs(msoa_london)
Coordinate Reference System:
No EPSG code
proj4string: "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +units=m +no_defs"
st_crs(lfb_sf)
Coordinate Reference System:
EPSG: 27700
proj4string: "+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs"
msoa_london <- st_transform(msoa_london, crs = 27700)
lfb_msoa_sf <- st_join(lfb_sf, msoa_london)
head(lfb_msoa_sf)
Simple feature collection with 6 features and 12 fields
geometry type: POINT
dimension: XY
bbox: xmin: 504650 ymin: 164950 xmax: 554650 ymax: 192350
epsg (SRID): 27700
proj4string: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs
Before we proceed let’s check if all points were succesfully joined. If that’s not the case, some observations will be NA.
Exercise - remove NA joins
- Use
filter() and is.na() on the msoa11cd variable of lfb_msoa_sf to check if any points did not join correctly.
- Save those to a new object called
lfb_msoa_sf_na
- Create an interactive map of the points and see why they did not join correctly.
- Remove all
NA observations from lfb_msoa_sfand overwrite it - use !is.na to find the correct subset.
Solution
tmap_mode("view")
tmap mode set to interactive viewing
lfb_sf_na <- filter(lfb_msoa_sf, is.na(msoa11cd))
qtm(lfb_sf_na)
lfb_msoa_sf <- filter(lfb_msoa_sf, !is.na(msoa11cd))
Now that msoa11cd is attached to our observations we can create some summary statistics for each MSOA. As mentioned before, we can use standard tidyverse functions on sf objects. Here, we will use dplyr to calculate the total number of incidents and their cost, and then use a non spatial join to attach those results to MSOA boundaries. At this stage we no longer need the geometry column for each LFB incident as a) we’re not performing any spatial operations on our points, and b) the geometry column can slow down/interrupt the dplyr::group_by function which we will be using. To remove the geometry column we can use the st_drop_geometry() function directly in the dplyr pipe.
Exercise - MSOA summary statistics
- The step requires you to be familiar with
dplyr’s more advanced functions. If you are struggling with this step load data/gpkg/msoa_lfb.gpkg as msoa_lfb using st_read.
- Use
st_drop_geometry() on lfb_msoa_sf to remove geometry data.
- Create summary statistics per MSOA - sum of cost_gbp as total_cost (use
na.rm = TRUE), and the total number of incidents as n_cases. You will need to use group_by() and summarise()
- Create a new column called
cost_per_incident using mutate - total_cost divided by n_cases.
- Join
lfb_msoa_stats to msoa_london, using left_join() and create a new object msoa_lfb
lfb_msoa_stats <- lfb_msoa_sf %>%
st_drop_geometry() %>%
group_by(msoa11cd) %>%
summarise(total_cost = sum(cost_gbp, na.rm=TRUE), n_cases = n()) %>%
mutate(cost_per_incident = total_cost/n_cases)
msoa_lfb <- left_join(msoa_london, lfb_msoa_stats)
msoa_lfb
Simple feature collection with 983 features and 5 fields
geometry type: MULTIPOLYGON
dimension: XY
bbox: xmin: 503574.2 ymin: 155850.8 xmax: 561956.7 ymax: 200933.6
epsg (SRID): 27700
proj4string: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 +units=m +no_defs
First 10 features:
msoa11cd msoa11nm total_cost n_cases cost_per_incident geometry
1 E02000001 City of London 001 3750 12 312.5000 MULTIPOLYGON (((531667.6 18...
2 E02000002 Barking and Dagenham 001 2221 7 317.2857 MULTIPOLYGON (((548881.6 19...
3 E02000003 Barking and Dagenham 002 4779 11 434.4545 MULTIPOLYGON (((549102.4 18...
4 E02000004 Barking and Dagenham 003 2917 7 416.7143 MULTIPOLYGON (((551550 1873...
5 E02000005 Barking and Dagenham 004 1113 4 278.2500 MULTIPOLYGON (((549099.6 18...
6 E02000007 Barking and Dagenham 006 2756 9 306.2222 MULTIPOLYGON (((549819.9 18...
7 E02000008 Barking and Dagenham 007 3205 10 320.5000 MULTIPOLYGON (((548171.4 18...
8 E02000009 Barking and Dagenham 008 2067 7 295.2857 MULTIPOLYGON (((546855 1863...
9 E02000010 Barking and Dagenham 009 859 3 286.3333 MULTIPOLYGON (((549618.8 18...
10 E02000011 Barking and Dagenham 010 550 2 275.0000 MULTIPOLYGON (((550244.1 18...
msoa_lfb <- st_read("data/gpkg/msoa_lfb.gpkg")
At this stage it is a good idea to save our data. We can do this using the st_write() function. It needs an sf object and the path and name of the output.
Exercise - save data to gpkg
- Copy and execute the following code to save your data:
st_write(msoa_lfb,"output/msoa_lfb.gpkg)
Making better maps
Now that we have processed our data we can start mapping it. So far we have only used the qtm() function from the tmap package. This creates a default map and is great when all we want to do is quickly visualise our data. The full range of tmap functions gives us control over all elements of the final plot and allows us to create high quality maps.
tmap_mode("plot")
tm_shape(msoa_lfb) +
tm_polygons(col = "total_cost", border.col = "#4a4949", lwd = 0.05, title = "Total cost (£)", palette = "Blues", contrast = 1, legend.hist = TRUE,
labels = c("0 - 2,000", ">2,000 - 4,000", ">4,000 - 6,000", ">6,000 - 8,000", ">8,000 - 10,000",
">10,000 - 12,000", ">12,000 - 14,000")) +
tm_scale_bar(position = c(0,0), text.size = 0.7) +
tm_layout(main.title = "Cost of animal related incidents per MSOA, between 2009 and 2020", main.title.position = c(0,0), main.title.size = 1, main.title.fontface = "bold", frame = FALSE, legend.position = c(0.08,0.18),
inner.margins = c(0.1,0.05,0.1,0.02), legend.outside = TRUE, legend.title.size = 1, legend.text.size = 0.7, title.snap.to.legend = FALSE) +
tm_shape(london_boundary) + tm_borders(col = "black", lwd = 0.25)

tmap follows similar principles to ggplot2, where we first specify the data source - tm_shape, then the aesthetics of the plot - tm_polygons, tm_dots, etc., and then we make any finaly adjustments - tm_layout. All functions need to be connected using the + symbol.
tm_shape() - sf object which you want to plot
tm_fill(), tm_borders(), tm_polygons(), tm_dots() - types of output
tm_layout() - controls layout of the map, titles, labels, etc.
tmap syntax: tm_shape(sf_object) + tm_borders(col = either "colour" or name of column which we want to plot) + tm_layout(main.title = "title of your map")
Guided exercise - mapping
Start by specifying which sf object is being mapped in tm_shape() and what column holds the values to be visualised. We will also change the legend’s title.
tm_shape(msoa_lfb) +
tm_polygons(col = "cost_per_incident", title = "Cost per Incident (£)")

Now let’s add london_boundary to have a thicker line around London.
tm_shape(msoa_lfb) +
tm_polygons(col = "cost_per_incident", title = "Cost per Incident (£)") +
tm_shape(london_boundary) + tm_borders(col = "black")

Next we will add a scale bar and position it in the bottom left corner.
tm_shape(msoa_lfb) +
tm_polygons(col = "cost_per_incident", title = "Cost per Incident (£)") +
tm_shape(london_boundary) + tm_borders(col = "black") +
tm_scale_bar(position = c(0,0))

We can now remove the black frame from the map and add a title to our map.
tm_shape(msoa_lfb) +
tm_polygons(col = "cost_per_incident", title = "Cost per Incident (£)") +
tm_shape(london_boundary) + tm_borders(col = "black") +
tm_scale_bar(position = c(0,0)) +
tm_layout(title = "Average cost of animal related incidents between 2009 and 2020",
frame = FALSE)

All of the map elements are now visible but they’re not in the right place. We can solve this by increasing the margins around our map. This will allow the title and the legend to move outwards.
tm_shape(msoa_lfb) +
tm_polygons(col = "cost_per_incident", title = "Cost per Incident (£)") +
tm_shape(london_boundary) + tm_borders(col = "black") +
tm_scale_bar(position = c(0,0)) +
tm_layout(title = "Average cost of animal related incidents between 2009 and 2020",
frame = FALSE, inner.margins = c(0.1,0.1,0.1,0.15))

We can also manually change the legend labels to ensure there are no overlapping values.
tm_shape(msoa_lfb) +
tm_polygons(col = "cost_per_incident", title = "Cost per Incident (£)",
labels = c("0 - 200", ">200 - 400", ">400 - 600", ">600 - 800", ">800 - 1,000",
">1,000 - 1,200")) +
tm_shape(london_boundary) + tm_borders(col = "black") +
tm_scale_bar(position = c(0,0)) +
tm_layout(title = "Average cost of animal related incidents between 2009 and 2020",
frame = FALSE, inner.margins = c(0.1,0.1,0.1,0.15))

Finally let’s change the colour of our map and increase the contrast. Choose a colour from R Colours.
tm_shape(msoa_lfb) +
tm_polygons(col = "cost_per_incident", title = "Cost per Incident (£)",
labels = c("0 - 200", ">200 - 400", ">400 - 600", ">600 - 800", ">800 - 1,000",
">1,000 - 1,200"), palette = "Blues", contrast = 1) +
tm_shape(london_boundary) + tm_borders(col = "black") +
tm_scale_bar(position = c(0,0)) +
tm_layout(title = "Average cost of animal related incidents between 2009 and 2020",
frame = FALSE, inner.margins = c(0.1,0.1,0.1,0.15))

Finally, save your map as an R object and export it.
average_cost <- tm_shape(msoa_lfb) +
tm_polygons(col = "cost_per_incident", title = "Cost per Incident (£)", palette = "Blues", contrast = 1) +
tm_shape(london_boundary) + tm_borders(col = "black") +
tm_scale_bar(position = c(0,0)) +
tm_layout(title = "Average cost of animal related incidents between 2009 and 2020",
frame = FALSE, inner.margins = c(0.1,0.1,0.1,0.15))
tmap_save(average_cost, "output/maps/average_cost_msoa.png", width = 8, height = 5)
You can also view your choropleth as an interactive map. It helps to add an alpha argument to change your map’s transparency.
tmap_mode("view")
tmap mode set to interactive viewing
tm_shape(msoa_lfb) +
tm_polygons(col = "cost_per_incident", title = "Cost per Incident (£)", palette = "Blues", contrast = 1, alpha = 0.5) +
tm_shape(london_boundary) + tm_borders(col = "black")
LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBHSVMgaW4gUiINCmF1dGhvcjogIlJvYmVydCBLYWxldGEiDQpkYXRlOiAiRmVicnVhcnkgMjAyMCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogMw0KICAgIHRvY19mbG9hdDogeWVzDQojICBodG1sX2RvY3VtZW50Og0KIyAgICBkZl9wcmludDogcGFnZWQNCiMgICAgdG9jOiB5ZXMNCiMgICAgdG9jX2RlcHRoOiAnMycNCi0tLQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmh0bWx0b29sczo6aW1nKHNyYyA9IGtuaXRyOjppbWFnZV91cmkoIkQ6LzNfT05TX0RPQ1VNRU5UUy9nZW9zcGF0aWFsX2xvZ29zL3NtYWxsIGdlbyBpY29uLnBuZyIpLCANCiAgICAgICAgICAgICAgIGFsdCA9ICdsb2dvJywgDQogICAgICAgICAgICAgICBzdHlsZSA9ICdwb3NpdGlvbjphYnNvbHV0ZTsgdG9wOjUwcHg7IHJpZ2h0OjA7IHBhZGRpbmc6MTBweDsnKQ0KYGBgDQoNCmBgYHtyLCBlY2hvPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5hbGlnbj0iY2VudGVyIikNCmBgYA0KDQpgYGB7ciBsaWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89RkFMU0V9DQpsaWJyYXJ5KCJzZiIpDQpsaWJyYXJ5KCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSgidG1hcCIpDQpsaWJyYXJ5KCJrbml0ciIpDQpgYGANCg0KIyBTZXQgdXAgUg0KDQoqIEdvIHRvIFtHaXRodWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9PTlNHZW9zcGF0aWFsL0ludHJvZHVjdGlvbl9HSVNfUikgYW5kIGRvd25sb2FkIHRoZSByZXBvc2l0b3J5IGFzIGEgemlwIGZpbGUuIA0KKiBNb3ZlIGl0IHRvIHlvdXIgbG9jYWwgZHJpdmUuDQoqIEluIFJTdHVkaW8gZ28gdG8gRmlsZSAtPiBOZXcgUHJvamVjdCAtPiBFeGlzdGluZyBEaXJlY3RvcnkgLT4gRG93bmxvYWRlZCBHaXRodWIgcmVwb3NpdG9yeS4NCiogUnVuIGBnZXR3ZCgpYCBpbiB5b3VyIFIgY29uc29sZSBhbmQgY2hlY2sgaWYgdGhlIHBhdGggZW5kcyBpbiAiSW50cm9fdG9fUl9naXMiLg0KKiBPcGVuIGEgbmV3IHNjcmlwdDogRmlsZSAtPiBOZXcgRmlsZSAtPiBSIFNjcmlwdCAtIHRoaXMgaXMgd2hlcmUgeW91IHdpbGwgd3JpdGUgYWxsIG9mIHRvZGF5J3MgY29kZS4NCiogV29ya2luZyBpbiBhbiBSIHByb2plY3QgbWVhbnMgdGhhdCBvdXIgZmlsZSBwYXRocyBhcmUgYWxsIHJlbGF0aXZlIHRvIHRoZSAiSW50cm9fdG9fUl9HSVMiIGZvbGRlci4NCg0KIyMgUXVpY2sgUiBiYXNpY3MNCg0KKiBSIGlzIGNhc2Ugc2Vuc2l0aXZlIC0gYHJlYWRfY3N2YCBpcyBub3QgdGhlIHNhbWUgYXMgYHJlYWRfQ1NWYC4NCiogTmV3IG9iamVjdHMgYXJlIGNyZWF0ZWQgdXNpbmcgdGhlIGA8LWAgbm90YXRpb24sIGUuZyBgbmV3X29iamVjdCA8LSAyICogNWAgLg0KKiBUbyBvdmVyd3JpdGUgYW4gb2JqZWN0IHVzZSBgPC1gIGFuZCBpdHMgY3VycmVudCBuYW1lLCBlLmcuIGBjdXJyZW50X29iamVjdCA8LSBzdF91bmlvbihjdXJyZW50X29iamVjdClgLg0KKiBGdW5jdGlvbidzIGFyZ3VtZW50cyBoYXZlIHRvIGJlIGluIGAoKWAgYW5kIHRoZXkncmUgZGVmaW5lZCB3aXRoIGEgYD1gLCBlLmcuIGBzdF90cmFuc2Zvcm0oeCA9IGxmYl9zZiwgY3JzID0gMjc3MDApYA0KKiBUbyBzZWUgZnVuY3Rpb24ncyBkb2N1bWVudGF0aW9uIHByZWNlZWQgaXRzIG5hbWUgd2l0aCBhIGA/YCwgZS5nIGA/c3RfYXNfc2ZgLg0KKiBXcml0ZSBpbiB0aGUgY29kZSBlZGl0b3IgKHNjcmlwdCkgYW5kIGV4ZWN1dGUgeW91ciBjb2RlIGxpbmUgYnkgbGluZS4NCiogVXNlIGBDdHJsICsgRW50ZXJgIHRvIGV4ZWN1dGUgdGhlIGN1cnJlbnQgbGluZSBvZiBjb2RlLg0KDQojIyBJbnN0YWxsICYgbG9hZCBSIGxpYnJhcmllcw0KDQpJZiB5b3UgaGF2ZSBub3QgaW5zdGFsbGVkIHRoZSBuZWNlc3NhcnkgcGFja2FnZXMgcnVuIGBpbnN0YWxsLnBhY2thZ2VzKClgIGFuZCB0aGVuIGxvYWQgdGhlbSB1c2luZyB0aGUgYGxpYnJhcnkoKWAgZnVuY3Rpb24uDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmluc3RhbGwucGFja2FnZXMoInNmIiwgZGVwZW5kZW5jaWVzID0gVFJVRSwgdHlwZSA9ICJ3aW4uYmluYXJ5IikNCmluc3RhbGwucGFja2FnZXMoInRtYXAiLCBkZXBlbmRlbmNpZXMgPSBUUlVFLCB0eXBlID0gIndpbi5iaW5hcnkiKQ0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIiwgZGVwZW5kZW5jaWVzID0gVFJVRSwgdHlwZSA9ICJ3aW4uYmluYXJ5IikNCg0KbGlicmFyeShzZikgDQpsaWJyYXJ5KHRtYXApDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQoNCiMgQWltcw0KDQoqKkJ5IHRoZSBlbmQgb2YgdG9kYXkgeW91IHdpbGw6ICoqDQogDQoqIFVuZGVyc3RhbmQgd2hhdCBzcGF0aWFsIGRhdGEgYW5kIEdJUyBhcmUuDQoqIEtub3cgaG93IHRvIHVzZSB0aGUgT3BlbiBHZW9ncmFwaHkgUG9ydGFsLg0KKiBCZSBhd2FyZSBvZiBtYXAgcHJvamVjdGlvbnMgYW5kIENvb3JkaW5hdGUgUmVmZXJlbmNlIFN5c3RlbXMgKENSUykgYW5kIGJlIGFibGUgdG8gbW9kaWZ5IHRoZW0uDQoqIEJlIGFibGUgdG8gbG9hZCBzcGF0aWFsIGRhdGEgaW50byBSIHVzaW5nIHRoZSBgc2ZgIGxpYnJhcnkuDQoqIEJlIGZhbWlsaWFyIHdpdGggdXNpbmcgR1NTIGNvZGVzIHRvIGpvaW4gc3RhdGlzdGljcyB0byBnZW9ncmFwaGllcy4NCiogVW5kZXJzdGFuZCBob3cgc3BhdGlhbCBvYmplY3RzIGNhbiBiZSBtYW5pcHVsYXRlZCB1c2luZyBSJ3MgYHRpZHl2ZXJzZWAuDQoqIFVuZGVyc3RhbmQgaG93IHRvIHVzZSBzcGF0aWFsIGpvaW5zLiANCiogS25vdyBob3cgdG8gbWFrZSBzdGF0aWMgYW5kIGludGVyYWN0aXZlIG1hcHMgaW4gYHRtYXBgLg0KKiBCZSBhYmxlIHRvIGV4cG9ydCB5b3VyIG1hcHMgYW5kIHNoYXBlZmlsZXMuDQoNCg0KIyBHSVMgJiBTcGF0aWFsIERhdGENCg0KKipHSVMqKiBzdGFuZHMgZm9yIEdlb2dyYXBoaWMgSW5mb3JtYXRpb24gU3lzdGVtL1NjaWVuY2UgLSB0aGUgU3lzdGVtIHBhcnQgcmVmZXJzIHRvIHRoZSBzb2Z0d2FyZSB1c2VkIGZvciBjYXB0dXJpbmcsIHN0b3JpbmcsIGFuZCBtYW5pcHVsYXRpbmcgc3BhdGlhbCBkYXRhLCB3aGlsZSB0aGUgU2NpZW5jZSBkZWZpbml0aW9uIGlzIGNvbmNlcm5lZCB3aXRoIHNjaWVudGlmaWMgcHJpbmNpcGxlcyBiZWhpbmQgc3BhdGlhbCBhbmFseXNpcywgYW5kIGRldmVsb3BpbmcgbmV3IG1ldGhvZHMgYW5kIGFwcHJvYWNoZXMgdG8gZXh0cmFjdCBpbnNpZ2h0IGZyb20gZ2VvZ3JhcGhpYyBkYXRhLiANCg0KPGNlbnRlcj4NCiFbXShkYXRhL2ltZy9naXNfbG9nb3MucG5nKQ0KPC9jZW50ZXI+DQoNCioqU3BhdGlhbCBEYXRhKiogLSBhbnkgZGF0YSBzZXQgd2hpY2ggaGFzLCBvciBoYXMgdGhlIHBvdGVudGlhbCB0byBoYXZlLCBsb2NhdGlvbiBhdHRhY2hlZCB0byBpdC4gVGhpcyBpbmNsdWRlcywgYnV0IGlzIG5vdCBsaW1pdGVkIHRvLCBjb29yZGluYXRlcywgYWRkcmVzc2VzLCBhbmQgZ2VvZ3JhcGh5IGNvZGVzLg0KDQoqKlNwYXRpYWwgZGF0YSB0eXBlczoqKg0KDQoqIFZlY3RvciAtIHBvaW50cywgbGluZXMsIGFuZCBwb2x5Z29ucyB1c2VkIHRvIHJlcHJlc2VudCBwaHlzaWNhbCBhbmQgYWRtaW5pc3RyYXRpdmUgZmVhdHVyZXMuIFVzZWQgZm9yIGRpc3BsYXlpbmcgZGF0YSB3aXRoIHdlbGwgZGVmaW5lZCBleHRlbnQuDQoNCjxjZW50ZXI+DQohW10oZGF0YS9pbWcvdmVjdG9yX2V4YW1wbGUucG5nKQ0KPC9jZW50ZXI+DQoNCiogUmFzdGVyIC0gcGl4ZWwgYmFzZWQgZGF0YSwgb2Z0ZW4gZGVyaXZlZCBmcm9tIHNhdGVsbGl0ZSBpbWFnZXJ5LiBVc2VkIGZvciBkaXNwbGF5aW5nIGNvbnRpbnVvdXMgb3IgZnV6enkgdmFyaWFibGVzLiANCg0KPGNlbnRlcj4NCiFbXShkYXRhL2ltZy9yYXN0ZXJfZXhhbXBsZV9zbWFsbC5wbmcpDQo8L2NlbnRlcj4NCg0KDQoNCg0KKipDb21tb24gc3BhdGlhbCBkYXRhIGZvcm1hdHM6KioNCg0KKiBTaGFwZWZpbGUgKC5zaHApIA0KKiBHZW9QYWNrYWdlICguZ3BrZykgLyBHZW9kYXRhYmFzZSAoLmdkYikNCiogR2VvSlNPTiAvIFRvcG9KU09ODQoqIFdlbGwta25vd24tdGV4dA0KKiBHZW9UaWZmDQoNCiMjIE1hcCBQcm9qZWN0aW9uIGFuZCBDb29yZGluYXRlIFJlZmVyZW5jZSBTeXN0ZW0NCg0KKkluIGNhcnRvZ3JhcGh5LCBhIG1hcCBwcm9qZWN0aW9uIGlzIGEgd2F5IHRvIGZsYXR0ZW4gYSBnbG9iZSdzIHN1cmZhY2UgaW50byBhIHBsYW5lIGluIG9yZGVyIHRvIG1ha2UgYSBtYXAuIFRoaXMgcmVxdWlyZXMgYSBzeXN0ZW1hdGljIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSBsYXRpdHVkZXMgYW5kIGxvbmdpdHVkZXMgb2YgbG9jYXRpb25zIGZyb20gdGhlIHN1cmZhY2Ugb2YgdGhlIGdsb2JlIGludG8gbG9jYXRpb25zIG9uIGEgcGxhbmUuIEFsbCBwcm9qZWN0aW9ucyBvZiBhIHNwaGVyZSBvbiBhIHBsYW5lIG5lY2Vzc2FyaWx5IGRpc3RvcnQgdGhlIHN1cmZhY2UgaW4gc29tZSB3YXkgYW5kIHRvIHNvbWUgZXh0ZW50LiBEZXBlbmRpbmcgb24gdGhlIHB1cnBvc2Ugb2YgdGhlIG1hcCwgc29tZSBkaXN0b3J0aW9ucyBhcmUgYWNjZXB0YWJsZSBhbmQgb3RoZXJzIGFyZSBub3Q7IHRoZXJlZm9yZSwgZGlmZmVyZW50IG1hcCBwcm9qZWN0aW9ucyBleGlzdCBpbiBvcmRlciB0byBwcmVzZXJ2ZSBzb21lIHByb3BlcnRpZXMgb2YgdGhlIHNwaGVyZS1saWtlIGJvZHkgYXQgdGhlIGV4cGVuc2Ugb2Ygb3RoZXIgcHJvcGVydGllcy4gRXZlcnkgZGlzdGluY3QgbWFwIHByb2plY3Rpb24gZGlzdG9ydHMgaW4gYSBkaXN0aW5jdCB3YXksIGJ5IGRlZmluaXRpb24uKg0KDQo8Zm9udCBzaXplPSIyIj5Tb3VyY2U6IFdpa2lwZWRpYSA8L2ZvbnQ+DQoNCjxjZW50ZXI+DQohW10oZGF0YS9pbWcvbWFwX3Byb2plY3Rpb24ucG5nKQ0KPC9jZW50ZXI+DQo8Zm9udCBzaXplPSIyIj5bU291cmNlXShodHRwczovL3d3dy5yZXNlYXJjaGdhdGUubmV0L3Byb2ZpbGUvQm9qYW5fU2F2cmljMi9wdWJsaWNhdGlvbi8yOTgzNTQyNzgvZmlndXJlL2ZpZzEvQVM6NjE0MzU0OTUzMTk5NjMxQDE1MjM0ODUwNDM0NzMvVGhlLW5pbmUtc21hbGwtLS1zY2FsZS1tYXAtcHJvamVjdGlvbnMtdXNlZC1pbi10aGUtcGFpcmVkLWNvbXBhcmlzb24tdGVzdC1hcnJhbmdlZC1ieS5wbmcpPC9mb250Pg0KDQoqKkFsbCB5b3UgbmVlZCB0byBrbm93IGZvciB0b2RheSBpczoqKg0KDQoqIFdoZW4gd29ya2luZyB3aXRoIEdCIGRhdGEgdXNlIHRoZSAqKkJyaXRpc2ggTmF0aW9uYWwgR3JpZCAoQk5HKSoqLg0KKiBCTkcgdXNlcyAqKkVhc3RpbmdzKiogYW5kICoqTm9ydGhpbmdzKiogd2hpY2ggYXJlIGdpdmVuIGFzIG1ldHJlcywgb2Zmc2V0IGZyb20gdGhlIG9yaWdpbiBwb2ludC4NCiogQk5HJ3MgRVBTRyBjb2RlIGlzICoqMjc3MDAqKi4NCg0KDQoNCjxjZW50ZXI+DQohW10oZGF0YS9pbWcvYm5nLnBuZykNCg0KPC9jZW50ZXI+DQoNCg0KIyMgR0lTIGFuZCBSDQoNClIgaXMgY29tbW9ubHkgdXNlZCBmb3Igc3RhdGlzdGljYWwgYW5hbHlzaXMgYW5kIHByb2dyYW1taW5nLCBob3dldmVyIGl0IGFsc28gaGFzIGEgd2hvbGUgcmFuZ2Ugb2YgR0lTIHRvb2xzLiBUaGVyZSBpcyBhIGxvbmcgaGlzdG9yeSBvZiBnZW9zcGF0aWFsIGxpYnJhcmllcyBiZWluZyBkZXZlbG9wZWQgZm9yIFIgYW5kIGFuIGFtYXppbmcgY29tbXVuaXR5IG9mIHJlc2VhcmNoZXJzIGFuZCBwcm9ncmFtbWVycyBhcm91bmQgaXQuIEluIHRoZSBsYXN0IGZldyB5ZWFycywgd29ya2luZyB3aXRoIHNwYXRpYWwgZGF0YSBiZWNhbWUgbXVjaCBlYXNpZXIgaW4gUiwgd2l0aCB0aGUgZGV2ZWxvcG1lbnQgb2YgdGhlIGBzZmAgcGFja2FnZS4gYHNmYCBrZWVwcyBhbGwgc3BhdGlhbCBpbmZvcm1hdGlvbiBmb3IgZWFjaCBvYnNlcnZhdGlvbiBpbiBhIGdlb21ldHJ5IGNvbHVtbiB3aGljaCBtZWFucyB0aGF0IHdlIGNhbiB0cmVhdCBpdCBsaWtlIGEgbm9ybWFsIGRhdGEgZnJhbWUgYnV0IGFsc28gcGVyZm9ybSBhbGwgdHlwZXMgb2Ygc3BhdGlhbCBvcGVyYXRpb25zIG9uIHRoZSBkYXRhLiAgDQoNCmBgYHtyIGVjaG89RkFMU0V9DQp3ZF8yMDE5X2JnYyA8LSBzdF9yZWFkKCJkYXRhL3NocC9XYXJkc19EZWNlbWJlcl8yMDE5X0JvdW5kYXJpZXNfRVdfQkdDL1dhcmRzX0RlY2VtYmVyXzIwMTlfQm91bmRhcmllc19FV19CR0Muc2hwIiwgcXVpZXQgPSBUUlVFKSAlPiUgDQogIHNlbGVjdCh3ZDE5Y2QsIHdkMTlubSkNCmhlYWQod2RfMjAxOV9iZ2MpDQpgYGANCg0KDQojIyBTb3VyY2VzIG9mIHNwYXRpYWwgZGF0YQ0KDQpXaGVuIHdvcmtpbmcgd2l0aCBtb3N0IHR5cGVzIG9mIGRhdGEgeW91IHdpbGwgbW9zdCBjb21tb25seSBuZWVkIHRvIGxpbmsgdGhlbSB0byBvZmZpY2lhbCBhZG1pbmlzdHJhdGl2ZSBhbmQgY2Vuc3VzIGJvdW5kYXJpZXMsIGFzIHdlbGwgYXMgbW9yZSBnZW5lcmFsIGdlb2dyYXBoaWMgZGF0YS4gTW9zdCBuYXRpb25hbCBhbmQgbG9jYWwgZ292ZXJubWVudHMgd2lsbCBoYXZlIGEgZ2VvZ3JhcGh5IHBvcnRhbCB3aGVyZSBib3VuZGFyaWVzIGNhbiBiZSBkb3dubG9hZGVkLiBBdCBPTlMgYm91bmRhcmllcywgbG9va3VwcywgYW5kIGRvY3VtZW50YXRpb24gY2FuIGJlIGRvd25sb2FkZWQgZnJvbSB0aGUgW09wZW4gR2VvZ3JhcGh5IFBvcnRhbF0oaHR0cDovL2dlb3BvcnRhbC5zdGF0aXN0aWNzLmdvdi51ay8pLCB3aGlsZSBvdGhlciBnZW9ncmFwaGljIGZlYXR1ZXMgY2FuIGJlIGFjY2Vzc2VkIGZyb20gdGhlIFtPcmRuYW5jZSBTdXJ2ZXldKGh0dHA6Ly9vcmRuYW5jZXN1cnZleS5jby51aykuICANCg0KPGNlbnRlcj4NCiFbXShkYXRhL2ltZy9nZW9fcG9ydGFsLnBuZykNCjwvY2VudGVyPiAgDQoNCg0KDQpCb3VuZGFyaWVzIG1hbmFnZWQgYnkgdGhlIE9OUyBjb21lIGluIHNldmVyYWwgcmVzb2x1dGlvbnMgKG1vcmUgZ2VuZXJhbGlzZWQgYm91bmRhcmllcyBzYWNyaWZpY2UgYWNjdXJhY3kgYW5kIHByZWNpc2lvbiBmb3IgZmlsZSBzaXplIGFuZCBwcm9jZXNzaW5nIHNwZWVkcyk6DQoNCiogRiAtIEZ1bGwgcmVzb2x1dGlvbg0KKiBHIC0gR2VuZXJhbGlzZWQNCiogVSAtIFVsdHJhIGdlbmVyYWxpc2VkDQoNCmFuZCBleHRlbnRzIChleHRlbnQgZGV0ZXJtaW5lcyB3aGVyZSB0aGUgbGFuZC93YXRlciBib3VuZGFyeSBpcyAtIGV4dGVudCBvZiB0aGUgcmVhbG0gaW5jbHVkZXMgYXJlYXMgb2Ygd2F0ZXIpOg0KDQoNCiogQyAtIENsaXBwZWQgdG8gY29hc3RsaW5lDQoqIEUgLSBFeHRlbnQgb2YgdGhlIHJlYWxtDQoNCg0KPGNlbnRlcj4NCiFbXShkYXRhL2ltZy9iZ2Nfc21hbGwucG5nKQ0KPC9jZW50ZXI+DQoNCg0KIyMjIEV4ZXJjaXNlIC0gT3BlbiBHZW9ncmFwaHkgUG9ydGFsDQoNCiogR28gdG8gdGhlIFtPcGVuIEdlb2dyYXBoeSBQb3J0YWxdKGh0dHA6Ly9nZW9wb3J0YWwuc3RhdGlzdGljcy5nb3YudWsvKSBhbmQgZG93bmxvYWQgdGhlIDIwMTkgTG9jYWwgQXV0aG9yaXR5IERpc3RyaWN0IEJHQyBib3VuZGFyaWVzLiBTYXZlIHRoZW0gdG8gYGRhdGEvc2hwYCBpbiB5b3VyIHByb2plY3Qgc3RydWN0dXJlIGFuZCB1bnppcC4gDQoqIE9wZW4gdGhlIGZvbGRlciBhbmQgc2VlIGhvdyBtYW55IGRpZmZlcmVudCBmaWxlcyB0aGVyZSBhcmUuDQoNCg0KIyBMb25kb24gRmlyZSBCcmlnYWRlIEFuaW1hbCBSZXNjdWUgRGF0YQ0KDQpUcm91Z2hvdXQgdGhpcyB0dXRvcmlhbCB3ZSB3aWxsIGJlIHVzaW5nIGRhdGEgZnJvbSB0aGUgTG9uZG9uIEZpcmUgQnJpZ2FkZSAtIFtMRkIgQW5pbWFsIFJlc2N1ZSBEYXRhXShodHRwczovL2RhdGEubG9uZG9uLmdvdi51ay9kYXRhc2V0L2FuaW1hbC1yZXNjdWUtaW5jaWRlbnRzLWF0dGVuZGVkLWJ5LWxmYikuIEl0IGNvdmVycyBhbGwgaW5jaWRlbnRzIGJldHdlZW4gMjAwOSBhbmQgMjAyMCB3aGljaCBpbmNsdWRlZCBhc3Npc3RhbmNlIHRvIGFuaW1hbHMgdGhhdCBtYXkgYmUgdHJhcHBlZCBvciBpbiBkaXN0cmVzcy4gVGhlIGRhdGEgaXMgdXBkYXRlZCBtb250aGx5IGFuZCBpbmNsdWRlcyBhIHJhbmdlIG9mIHZhcmlhYmxlcyBmb3IgZWFjaCBpbmNpZGVudCBpbmNsdWRpbmcgc29tZSBsb2NhdGlvbiBpbmZvcm1hdGlvbiAocG9zdGNvZGUsIGJvcm91Z2gsIHdhcmQpLHRoZSBkYXRlL3RpbWUgb2YgdGhlIGluY2lkZW50cywgY29zdCwgYW5kIHR5cGUgb2YgYW5pbWFsIGluIHRyb3VibGUuIA0KDQpXZSB3YW50IHRvIHZpc3VhbGlzZSwgYW5kIGJldHRlciB1bmRlcnN0YW5kIGhvdyBtdWNoIG1vbmV5IGhhcyBiZWVuIHNwZW50IG9uIGFuaW1hbCByZWxhdGVkIGluY2lkZW50cyBiZXR3ZWVuIDIwMDkgYW5kIDIwMjAsIGFuZCB3aGF0IHRoZSBkaXN0cmlidXRpb24gaXMgYXQgdGhlIE1TT0EgbGV2ZWwgb2YgZ2VvZ3JhcGh5LiBUbyBhY2hpZXZlIHRoaXMgd2Ugd2lsbCBoYXZlIHRvIGltcG9ydCBzcGF0aWFsIGRhdGEsIG1hbmlwdWxhdGUgaXQgYW5kIGNyZWF0ZSBzdW1tYXJ5IHN0YXRpc3RpY3MsIGFuZCB0aGVuIHBsb3QgaXQuDQoNCiMjIExvYWRpbmcgc3BhdGlhbCBhbmQgbm9uLXNwYXRpYWwgZGF0YQ0KDQpMRkIgZGF0YSBoYXMgYmVlbiB0aWRpZWQgdXAgYW5kIHNhdmVkIGFzIGEgQ29tbWEgU2VwYXJhdGVkIFZhbHVlIGZpbGUgKC5jc3YpLiBXZSBjYW4gdXNlIGByZWFkX2NzdmAgdG8gb3BlbiBpdCBpbiBSLg0KDQojIyMgRXhlcmNpc2UgLSBvcGVuIExGQiBkYXRhDQoNCiogQ3JlYXRlIGEgbmV3IG9iamVjdCBjYWxsZWQgYGxmYmAgYnkgdXNpbmcgYHJlYWRfY3N2KClgLiBMb2FkIGRhdGEgbG9jYXRlZCBpbiAiZGF0YS9jc3YvbGZiXzIwMDlfMjAyMC5jc3YiLg0KKiBVc2UgYGdsaW1wc2UoKWAgb3IgYGhlYWQoKWAgdG8gdmlldyBgbGZiYCBzdHJ1Y3R1cmUuDQoNCioqU29sdXRpb24qKg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCmxmYiA8LSByZWFkX2NzdigiZGF0YS9jc3YvbGZiXzIwMDlfMjAyMC5jc3YiKQ0KaGVhZChsZmIpDQpgYGANCg0KYGxmYmAgaXMgY3VycmVudGx5IGp1c3QgYSBkYXRhIGZyYW1lLiBJdCBoYXMgbm90IGdvdCBhbiBleHBsaWNpdCBnZW9tZXRyeSBjb2x1bW4gd2hpY2ggbGlua3Mgb2JzZXJ2YXRpb25zIHRvIHRoZWlyIGdlb2dyYXBoaWMgbG9jYXRpb24uIEl0IGRvZXMgaG93ZXZlciBjb250YWluIHNldmVyYWwgY29sdW1ucyB3aGljaCBjYW4gYmUgdXNlZCB0byBjb252ZXJ0IGl0IGludG8gYSBzcGF0aWFsIGRhdGEgZm9ybWF0LiAgIA0KDQoqKldhcmRfY29kZSoqIGNvbHVtbiByZWZlcmVuY2VzIHRoZSBHU1MgY29kZXMgb2Ygd2FyZHMgd2l0aGluIHdoaWNoIHRoZSBvYnNlcnZhdGlvbnMgZmFsbC4gR1NTIGNvZGVzIGNhbiBiZSB1c2VkIHRvIGpvaW4gYGxmYmAgZGF0YSB0byBib3VuZGFyaWVzIGZyb20gdGhlIE9wZW4gR2VvZ3JhcGh5IFBvcnRhbC4gT25lIGlzc3VlIHdpdGggdGhpcyBwYXJ0aWN1bGFyIGNvbHVtbiBpcyB0aGF0IGl0IGRvZXMgbm90IGluZGljYXRlIHRoZSBjdXJyZW5jeSBvZiBHU1MgY29kZXMuIFdhcmRzIGFyZSBzdWJqZWN0IHRvIGZyZXF1ZW50IGNoYW5nZSwgYW5kIGFzIHN1Y2ggaXQgaXMgYmVzdCBwcmFjdGljZSB0byBiZSBjbGVhciBhYm91dCB0aGUgZGF0ZXMgb2YgYW55IGJvdW5kYXJpZXMgdXNlZCBieSBzdGF0aW5nIHRoZSBleGFjdCBjb2RlIHVzZWQsIGUuZy4gKip3ZDE5Y2QqKi4gQmVjYXVzZSBMRkIgZGF0YSBkb2VzIG5vdCBpbmNsdWRlIHRoaXMgaW5mb3JtYXRpb24gd2UgaGF2ZSBubyBndWFyYW50ZWUgdGhhdCB0aGUgYm91bmRhcmllcyBhbmQgR1NTIGNvZGVzIHdlIGpvaW4gd2lsbCBtYXRjaC4gIA0KDQpGb3J0dW5hdGVseSB3ZSBoYXZlIGFsc28gYmVlbiBwcm92aWRlZCB3aXRoIGNvbHVtbnMgcmVjb3JkaW5nIHRoZSAqKmVhc3RpbmcqKiwgYW5kICoqbm9ydGhpbmcqKiBvZiBlYWNoIGluY2lkZW50LiBXZSBjYW4gdXNlIHRob3NlIHRvIGNvbnZlcnQgYGxmYmAgaW50byBhbiBgc2ZgIG9iamVjdC4gVG8gYWNoaWV2ZSB0aGlzIHdlIHdpbGwgdXNlIHRoZSBgc3RfYXNfc2YoKWAgZnVuY3Rpb24gd2hpY2ggdGFrZXMgdGhlIGZvbGxvd2luZyBhcmd1bWVudHM6DQoNCmBuZXdfb2JqZWN0IDwtIHN0X2FzX3NmKHggPSBpbnB1dF9kYXRhX2ZyYW1lLCBjb29yZHMgPSBjKCJ4X2Nvb3JkaW5hdGVfY29sdW1uIiwgInlfY29vcmRpbmF0ZV9jb2x1bW4iKSwgY3JzID0gMjc3MDApYA0KDQojIyMgRXhlcmNpc2UgLSBjcmVhdGUgc3BhdGlhbCBkYXRhDQoNCiogQ3JlYXRlIGEgbmV3IG9iamVjdCBjYWxsZWQgYGxmYl9zZmAgYnkgY29udmVydGluZyBgbGZiYCB1c2luZyB0aGUgYHN0X2FzX3NmKClgIGZ1bmN0aW9uLg0KKiBVc2UgYGdsaW1wc2UoKWAgb3IgYGhlYWQoKWAgdG8gdmlldyBgbGZiX3NmYCBzdHJ1Y3R1cmUuDQoNCioqU29sdXRpb24qKg0KYGBge3J9DQpsZmJfc2YgPC0gc3RfYXNfc2YoeCA9IGxmYiwgY29vcmRzID0gYygiZWFzdGluZyIsICJub3J0aGluZyIpLCBjcnMgPSAyNzcwMCkNCmhlYWQobGZiX3NmKQ0KYGBgDQoNCmBzdF9hc19zZigpYCBjb252ZXJ0ZWQgdGhlICoqZWFzdGluZyoqIGFuZCAqKm5vcnRoaW5nKiogY29sdW1ucyB0byBzaW1wbGUgZmVhdHVyZSBnZW9tZXRyaWVzIGFuZCBjcmVhdGVkIGEgbmV3IGNvbHVtbiBjYWxsZWQgKipnZW9tZXRyeSoqIHdoaWNoIGhvbGRzIHNwYXRpYWwgaW5mb3JtYXRpb24gZm9yIGVhY2ggcm93LiANCk5vdyB0aGF0IGBsZmJgIGlzIGEgc3BhdGlhbCBvYmplY3Qgd2UgY2FuIHBsb3QgaXQgdXNpbmcgdGhlIGB0bWFwYCBwYWNrYWdlLiBGb3Igbm93IHdlIHdpbGwgdXNlIHRoZSBgcXRtKClgIGZ1bmN0aW9uIHdoaWNoIGNyZWF0ZXMgYSBxdWljayBtYXAsIHVzaW5nIGB0bWFwJ3NgIGRlZmF1bHQgc2V0dGluZ3MuIGBxdG0oKWAgb25seSBuZWVkcyB0byBiZSBzdXBwbGllZCB3aXRoIGEgc2ltcGxlIGZlYXR1cmUgb2JqZWN0IGFuZCBpcyB2ZXJ5IHVzZWZ1bCBmb3IgcXVpY2tseSBpbnNwZWN0aW5nIHlvdXIgZGF0YS4gDQoNCiMjIyBFeGVyY2lzZSAtIHF1aWNrIHN0YXRpYyBtYXBzDQoNCiogUGxvdCBsZmJfc2YgdXNpbmcgdGhlIGBxdG0oKWAgZnVuY3Rpb24uDQoNCioqU29sdXRpb24qKg0KYGBge3J9DQpxdG0obGZiX3NmKQ0KYGBgDQoNCg0KV2UgY2FuIGFsc28gY3JlYXRlIGludGVyYWN0aXZlIG1hcHMgdXNpbmcgdGhlIGB0bWFwYCwgcGFja2FnZSBieSBydW5uaW5nIGB0bWFwX21vZGUoInZpZXciKWAgYmVmb3JlIGV4ZWN1dGluZyBgcXRtKClgLiANClRvIHJldmVyc2UgaXQgYW5kIGdvIGJhY2sgdG8gc3RhdGljIG1hcHMgdXNlIGB0bWFwX21vZGUoInBsb3QiKWAuDQoNCiMjIyBFeGVyY2lzZSAtIHF1aWNrIGludGVyYWN0aXZlIG1hcHMNCg0KKiBNYWtlIGFuIGludGVyYWN0aXZlIG1hcCBvZiBgbGZiX3NmYCB1c2luZyB0aGUgYHF0bSgpYCBmdW5jdGlvbiBhbmQgYHRtYXBfbW9kZSgidmlldyIpYC4NCg0KKipTb2x1dGlvbioqDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCnRtYXBfbW9kZSgidmlldyIpDQpxdG0obGZiX3NmKQ0KYGBgDQoNCiMjIEZpbHRlcmluZyBieSBHU1MgY29kZQ0KDQpJdCBsb29rcyBsaWtlIHNvbWUgb2YgdGhlIGxvY2F0aW9ucyBhcmUgbG9jYXRlZCBvdXRzaWRlIG9mIExvbmRvbiwgaG93ZXZlciB3ZSBhcmUgb25seSBpbnRlcmVzdGVkIGluIGluY2lkZW50cyB3aXRoaW4gdGhlIExvY2FsIEF1dGhvcml0eSBEaXN0cmljdHMgbWFraW5nIHVwIEdyZWF0ZXIgTG9uZG9uLiBUbyByZW1vdmUgYWxsIHBvaW50cyBvdXRzaWRlIG9mIExvbmRvbiB3ZSB3aWxsIGhhdmUgdG8gZmlyc3QgaW1wb3J0IHRoZSBMQUQgYm91bmRhcmllcyB3aGljaCB3ZSBkb3dubG9hZGVkIGZyb20gdGhlIE9wZW4gR2VvZ3JhcGh5IFBvcnRhbCBhbmQgdGhlbiB1c2UgdGhlbSB0byBzcGF0aWFsbHkgZmlsdGVyIGBsZmJfc2ZgIGRhdGEuIA0KDQpTbyBmYXIgd2UgaGF2ZSBjcmVhdGVkIG91ciBvd24gYHNmYCBvYmplY3RzIGJ5IGFkZGluZyBhIGdlb21ldHJ5IGNvbHVtbi4gVGhlIExBRCBkYXRhIHNldCBpcyBhbHJlYWR5IGEgc3BhdGlhbCBvbmUgYW5kIGFzIHN1Y2ggd2UgY2FuIHVzZSB0aGUgYHN0X3JlYWQoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYHNmYCBwYWNrYWdlIHRvIGltcG9ydCBpdC4gYHN0X3JlYWRgIGlzIGV4dHJlbWVseSB2ZXJzYXRpbGUgYW5kIGFibGUgdG8gaW1wb3J0IG1vc3Qgc3BhdGlhbCBkYXRhIGZvcm1hdHMgaW50byBSLiBUaGUgb25seSBhcmd1bWVudCB0aGF0IG5lZWRzIHRvIGJlIHN1cHBsaWVkIHRvIGBzdF9yZWFkYCBpcyB0aGUgZnVsbCBwYXRoIHRvIHRoZSBMQUQgYm91bmRhcmllcyANCg0KIyMjIEV4ZXJjaXNlIC0gbG9hZGluZyBzaGFwZWZpbGVzDQoNCiogVXNlIGBzdF9yZWFkKClgIHRvIGxvYWQgdGhlIExBRCBib3VuZGFyaWVzIHlvdSBkb3dubG9hZGVkIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIHR1dG9yaWFsLCBhc2BsYWRfMjAxOWAuDQoqIExBRCBwYXRoIC0gYGRhdGEvc2hwL0xvY2FsX0F1dGhvcml0eV9EaXN0cmljdHNfRGVjZW1iZXJfMjAxOV9Cb3VuZGFyaWVzX1VLX0JHQy9gDQogICAgICAgICAgICAgIGBMb2NhbF9BdXRob3JpdHlfRGlzdHJpY3RzX0RlY2VtYmVyXzIwMTlfQm91bmRhcmllc19VS19CR0Muc2hwYA0KKiBNYWtlIGEgc3RhdGljIG1hcCBvZiB0aGUgb2JqZWN0IHlvdSBoYXZlIGp1c3QgY3JlYXRlZCB1c2luZyBgcXRtKClgIGFuZCBzZXR0aW5nIGB0bWFwX21vZGUoInBsb3QiKWAuDQoNCioqU29sdXRpb24qKg0KYGBge3J9DQpsYWRfMjAxOSA8LSBzdF9yZWFkKCJkYXRhL3NocC9Mb2NhbF9BdXRob3JpdHlfRGlzdHJpY3RzX0RlY2VtYmVyXzIwMTlfQm91bmRhcmllc19VS19CR0MvTG9jYWxfQXV0aG9yaXR5X0Rpc3RyaWN0c19EZWNlbWJlcl8yMDE5X0JvdW5kYXJpZXNfVUtfQkdDLnNocCIpDQp0bWFwX21vZGUoInBsb3QiKQ0KcXRtKGxhZF8yMDE5KQ0KDQpgYGANCg0KTEFEIGJvdW5kYXJpZXMgaGF2ZSBsb2FkZWQgY29ycmVjdGx5IGJ1dCB0aGV5IGN1cnJlbnRseSBjb3ZlciBhbGwgb2YgdGhlIFVLIHdoZW4gYWxsIHdlIG5lZWQgaXMgTG9uZG9uLiBCZWNhdXNlIHNpbXBsZSBmZWF0dXJlIG9iamVjdHMgYXJlIGRhdGEgZnJhbWVzIHdpdGggYSBnZW9tZXRyeSBjb2x1bW4gYXR0YWNoZWQsIGFueSBvcGVyYXRpb25zIHRoYXQgd2Ugd291bGQgcGVyZm9ybSBvbiBhIG5vcm1hbCBkYXRhIGZyYW1lIGNhbiBhbHNvIGJlIHBlcmZvcm1lZCBvbiBhbiBvYmplY3Qgb2YgY2xhc3MgYHNmYC4gSGVyZSB3ZSB3aWxsIHVzZSB0aGUgYGRwbHlyOjpmaWx0ZXJgIGFuZCBgc3RyaW5ncjo6c3RyX2RldGVjdCgpYCBmcm9tIHRoZSB0aGUgYHRpZHl2ZXJzZWAgcGFja2FnZSB0byBvbmx5IGtlZXAgTEFEcyB3aG9zZSBHU1MgY29kZSBzdGFydHMgd2l0aCAiRTA5Ii4NCg0KDQojIyMgRXhlcmNpc2UgLSBmaWx0ZXIgc3BhdGlhbCBkYXRhIGJ5IHZhcmlhYmxlDQoNCiogSW5zcGVjdCBsYWRfMjAxOSB1c2luZyBgaGVhZCgpYCBvciBgZ2xpbXBzZSgpYCwgYW5kIGlkZW50aWZ5IHdoaWNoIGNvbHVtbiBob2xkcyB0aGUgR1NTIGNvZGVzIC0gaXQgc2hvdWxkIGVuZCBpbiAiY2QiLg0KKiBDcmVhdGUgYSBuZXcgb2JqZWN0IGNhbGxlZCBgbG9uZG9uX2xhZGAuIFVzZSBgZHBseXI6OmZpbHRlcmAgYWxvbmdzaWRlIGBzdHJpbmdyOjpzdHJfZGV0ZWN0KClgIHRvIG9ubHkga2VlcCBvYnNlcnZhdGlvbnMgd2hpY2ggaGF2ZSBhIEdTUyBjb2RlIHN0YXJ0aW5nIHdpdGggIkUwOSIuDQoqIFBsb3QgYGxvbmRvbl9sYWRgIHRvIHNlZSBpZiB0aGUgcmVzdWx0cyBsb29rIGNvcnJlY3QuDQoNCioqU29sdXRpb24qKg0KYGBge3J9DQpoZWFkKGxhZF8yMDE5KQ0KYGBgDQoNCmBgYHtyfQ0KbG9uZG9uX2xhZCA8LSBmaWx0ZXIobGFkXzIwMTksIHN0cl9kZXRlY3QobGFkMTljZCwgIkUwOSIpKQ0KcXRtKGxvbmRvbl9sYWQpDQpgYGANCg0KRmluYWxseSwgZm9yIHRoZSBuZXh0IHN0ZXAsIHdlIG9ubHkgbmVlZCB0aGUgb3V0ZXIgYm91bmRhcnkgb2YgTG9uZG9uIC0gYWxsIHRoZSBpbnRlcm5hbCBMQUQgYm91bmRhcmllcyBoYXZlIHRvIGJlIHJlbW92ZWQgYW5kIG9ubHkgdGhlIG91dGVyIGVkZ2VzIGtlcHQuIGBzZmAgaGFzIGEgZnVuY3Rpb24gZXhhY3RseSBmb3IgdGhpcyBwdXJwb3NlIGNhbGxlZCBgc3RfdW5pb24oKWAuIA0KSXQgb25seSB0YWtlcyBvbmUgYXJndW1lbnQsIHdoaWNoIGlzIHRoZSBgc2ZgIG9iamVjdCB3ZSB3YW50IHRvIHVuaW9uaXNlLiANCg0KIyMjIEV4ZXJjaXNlIC0gZGlzc29sdmUgYm91bmRhcmllcw0KDQoqIENyZWF0ZSBhIG5ldyBvYmplY3QgY2FsbGVkIGBsb25kb25fYm91bmRhcnlgIHVzaW5nIHRoZSBgc3RfdW5pb25gIGZ1bmN0aW9uLg0KKiBQbG90IGl0IHRvIGNoZWNrIHRoZSByZXN1bHRzLg0KDQoqKlNvbHV0aW9uKioNCmBgYHtyfQ0KbG9uZG9uX2JvdW5kYXJ5IDwtIHN0X3VuaW9uKGxvbmRvbl9sYWQpDQpxdG0obG9uZG9uX2JvdW5kYXJ5KQ0KYGBgDQoNCg0KIyMgU3BhdGlhbCBzdWJzZXR0aW5nIGFuZCBDUlMNCg0KSW4gYWRkaXRpb24gdG8gc3Vic2V0dGluZyBieSB2YWx1ZSwgYXMgd2UgZGlkIHdpdGggdGhlIExBRCBib3VuZGFyaWVzIGVhcmxpZXIsIHdlIGNhbiBhbHNvIHN1YnNldCBvYnNlcnZhdGlvbnMgYnkgZXZhbHVhdGluZyB0aGVpciBzcGF0aWFsIHJlbGF0aW9uc2hpcCB3aXRoIGFub3RoZXIgZGF0YSBzZXQuIFdlIGNhbiBmb3IgZXhhbXBsZSBzZWxlY3QgYWxsIExBRHMgd2hpY2ggYXJlIGZ1bGx5IHdpdGhpbiBXYWxlcywgZXZlcnkgT3V0cHV0IEFyZWEgaW50ZXJzZWN0ZWQgYnkgYSByaXZlciwgb3IgYWxsIGhvdXNlaG9sZHMgb3V0c2lkZSBvZiBjaXR5IGJvdW5kYXJpZXMuIFRoZXJlIGFyZSBhIG51bWJlciBvZiBkaWZmZXJlbnQgc3BhdGlhbCByZWxhdGlvbnNoaXBzIHdoaWNoIGNhbiBiZSB0ZXN0ZWQgYW5kIHVzZWQgdG8gc3Vic2V0IG9ic2VydmF0aW9ucy4NCg0KPGNlbnRlcj4NCiFbXShkYXRhL2ltZy9zcGF0aWFsX3JlbGF0aW9uLnBuZykgDQo8L2NlbnRlcj4NCg0KDQpgc2ZgIGhhcyBhbiBpbmJ1aWx0IGZ1bmN0aW9uIGNhbGxlZCBgc3RfZmlsdGVyKClgIHdoaWNoIHdlIGNhbiB1c2UgdG8gc3BhdGlhbGx5IHN1YnNldCBvYnNlcnZhdGlvbnMuDQpUaGUgZnVuY3Rpb24gdGFrZXMgc2V2ZXJhbCBhcmd1bWVudHM6DQoNCiogeCAtIGBzZmAgZGF0YSBmcmFtZSB3ZSB3YW50IHRvIHN1YnNldCAtIGBsZmJfc2ZgDQoqIHkgLSBgc2ZgIG9iamVjdCB1c2VkIHRvIGV2YWx1YXRlIHRoZSBzcGF0aWFsIHJlbGF0aW9uc2hpcCAtIGBsb25kb25fYm91bmRhcnlgDQoNCkJlZm9yZSBydW5uaW5nIGFueSBzcGF0aWFsIG9wZXJhdGlvbnMgb24gdHdvIHNwYXRpYWwgb2JqZWN0cyBpdCBpcyBhbHdheXMgd29ydGggY2hlY2tpbmcgaWYgdGhlaXIgY29vcmRpbmF0ZSByZWZlcmVuY2Ugc3lzdGVtcyAoQ1JTKSBtYXRjaC4gYHNmYCB3aWxsIHRocm93IGFuIGVycm9yIGlmIHRoYXQncyBub3QgdGhlIGNhc2UuIFRyeSBpdCBmb3IgeW91cnNlbGYgYmVsb3cuDQoNCiMjIyBFeGVyY2lzZSAtIHNwYXRpYWwgc3Vic2V0IHBhcnQgMSANCg0KKiBVc2UgYHN0X2ZpbHRlcigpYCB0byBzcGF0aWFsbHkgc3Vic2V0IGBsZmJfc2ZgIGJ5IHRlc3RpbmcgaXRzIHJlbGF0aW9uc2hpcCB3aXRoIGBsb25kb25fYm91bmRhcnlgLg0KDQoqKlNvbHV0aW9uOioqDQpgYGB7ciwgZXZhbD1GQUxTRX0NCmxmYl9zZiA8LSBzdF9maWx0ZXIoeCA9IGxmYl9zZiwgeSA9IGxvbmRvbl9ib3VuZGFyeSkNCg0KYGBgDQoNCllvdSBzaG91bGQgaGF2ZSBnb3QgYW4gZXJyb3IgaGVyZSBzYXlpbmcgYEVycm9yIGluICFpbmhlcml0cyh4LCAic2YiKSA6IHN0X2Nycyh4KSA9PSBzdF9jcnMoeSkgaXMgbm90IFRSVUVgLiBJdCBtZWFucyB0aGF0IG9iamVjdHMgeCBhbmQgeSBoYXZlIGRpZmZlcmVudCBDUlMuIFdlIGNhbiBzZWUgdGhpcyBmb3Igb3Vyc2VsdmVzIGJ5IHJ1bm5pbmcgdGhlIGBzdF9jcnMoKWAgZnVuY3Rpb24sIHdoaWNoIHJldHVybnMgdGhlIGNvb3JkaW5hdGUgcmVmZXJlbmNlIHN5c3RlbSBvZiBhbiBvYmplY3QuDQoNCiMjIyBFeGVyY2lzZSAgLSBjaGVjayBDUlMNCg0KKiBSdW4gYHN0X2NycygpYCBvbiBib3RoIGFuZCBgbGZiX3NmYCBhbmQgYGxvbmRvbl9ib3VuZGFyeWAgYW5kIGNvbXBhcmUgdGhlIHJlc3VsdHMuDQoNCioqU29sdXRpb246KioNCmBgYHtyfQ0Kc3RfY3JzKGxmYl9zZikNCg0Kc3RfY3JzKGxvbmRvbl9ib3VuZGFyeSkNCg0KYGBgDQoNCldlIGNhbiBzZWUgdGhhdCBgbG9uZG9uX2JvdW5kYXJ5YCBoYXMgbm90IGdvdCBhbiBFUFNHIGNvZGUsIGFuZCB0aGF0IHRoZSBwcm9qNHN0cmluZyBpbmZvcm1hdGlvbiBpcyBkaWZmZXJlbnQuIFdlIGNhbiAgc29sdmUgdGhpcyBwcm9ibGVtIGJ5IHRyYW5zZm9ybWluZyBgbG9uZG9uX2JvdW5kYXJ5YCdzIENSUyB0byBtYXRjaCB0aGF0IG9mIGBsZmJfc2ZgLCBzaW1wbHkgYnkgdXNpbmcgdGhlIGNvcnJlY3QgRVBTRyBjb2RlLiBUbyBkbyBzbyB3ZSB3aWxsIHVzZSB0aGUgYHN0X3RyYW5zZm9ybSgpYCBmdW5jdGlvbiB3aGljaCB0YWtlcyB0d28gYXJndW1lbnRzOiANCg0KKiB4IC0gYHNmYCBvYmplY3QgdG8gYmUgdHJhbnNmb3JtZWQNCiogY3JzIC0gRVBTRyBjb2RlIHRoYXQgd2Ugd2FudCB0byB0cmFuc2Zvcm0gb3VyIGRhdGEgdG8gLSBCTkcgaXMgMjc3MDAuDQoNCiMjIyBFeGVyY2lzZSAtIHRyYW5zZm9ybSBDUlMNCg0KKiBSdW4gYHN0X3RyYW5zZm9ybSgpYCB0byB0cmFuc2Zvcm0gYW5kIG92ZXJ3cml0ZSBgbG9uZG9uX2JvdW5kYXJ5YC4gUmVtZW1iZXIgdG8gc2V0IHRoZSBjb3JyZWN0IENSUy4gDQoqIFJ1biBgc3RfY3JzKClgIG9uIGBsZmJfc2ZgIGFuZCBuZXdseSB0cmFuc2Zvcm1lZCBgbG9uZG9uX2JvdW5kYXJ5YCBhbmQgY29tcGFyZSB0aGUgcmVzdWx0cy4NCg0KKipTb2x1dGlvbjoqKg0KDQpgYGB7cn0NCmxvbmRvbl9ib3VuZGFyeSA8LSBzdF90cmFuc2Zvcm0obG9uZG9uX2JvdW5kYXJ5LCBjcnMgPSAyNzcwMCkNCg0Kc3RfY3JzKGxmYl9zZikNCnN0X2Nycyhsb25kb25fYm91bmRhcnkpDQpgYGANCg0KDQpOb3cgdGhhdCB0aGUgQ1JTIGFyZSBtYXRjaGluZyB3ZSBzaG91bGQgYmUgYWJsZSB0byBzcGF0aWFsbHkgc3Vic2V0IGBsZmJfc2ZgLg0KDQojIyMgRXhlcmNpc2UgLSBzcGF0aWFsIHN1YnNldCBwYXJ0IDIgDQoNCiogVXNlIGBzdF9maWx0ZXJgIHRvIHNwYXRpYWxseSBzdWJzZXQgYGxmYl9zZmAgYnkgdGVzdGluZyBpdHMgcmVsYXRpb25zaGlwIHdpdGggYGxvbmRvbl9ib3VuZGFyeWAuIE92ZXJ3cml0ZSBgbGZiX3NmYCB3aXRoIHRoZSBzdWJzZXQgZGF0YS4NCiogUGxvdCBpdCB0byBjaGVjayBpZiB0aGUgcmVzdWx0cyBhcmUgY29ycmVjdC4NCg0KKipTb2x1dGlvbjoqKg0KYGBge3J9DQpsZmJfc2YgPC0gc3RfZmlsdGVyKHggPSBsZmJfc2YsIHkgPSBsb25kb25fYm91bmRhcnkpDQpxdG0obGZiX3NmKQ0KYGBgDQoNCg0KIyMgU3BhdGlhbCBhbmQgbm9uLXNwYXRpYWwgam9pbnMNCg0KU2ltcGxlIGZlYXR1cmVzIGRhdGEgY2FuIGJlIGpvaW5lZCB0byBvdGhlciBkYXRhIHNldHMgaW4gdHdvIHdheXMuIFdlIGNhbiBlaXRoZXIgdXNlIGEgdHJhZGl0aW9uYWwsIFNRTCBsaWtlIGpvaW4sIGJhc2VkIG9uIGEgdmFsdWUgc2hhcmVkIGFjcm9zcyB0aGUgZGF0YSBzZXRzIG9yLCBzaW5jZSB3ZSBoYXZlIGEgZ2VvbWV0cnkgY29sdW1uLCBvbiB0aGUgc3BhdGlhbCByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgZGF0YSBzZXRzLiBUaGlzIGlzIGtub3duIGFzIGEgc3BhdGlhbCBqb2luLCB3aGVyZSB2YXJpYWJsZXMgZnJvbSBvbmUgZGF0YSBzZXQgYXJlIGpvaW5lZCB0byBhbm90aGVyIG9uZSBvbmx5IG9uIHRoZSBiYXNpcyBvZiB0aGVpciBzcGF0aWFsIHJlbGF0aW9uc2hpcC4gVGhlIG1vc3QgY29tbW9ubHkgdXNlZCBvcGVyYXRpb24gaXMga25vd24gYXMgYSBQb2ludC1pbi1Qb2x5Z29uIGpvaW4gd2hlcmUgZGF0YSBmcm9tIGEgcG9seWdvbiBpcyBqb2luZWQgdG8gdGhlIHBvaW50cyB3aXRoaW4gdGhlbS4NCg0KPGNlbnRlcj4NCiFbXShkYXRhL2ltZy9zcGF0aWFsX2pvaW4ucG5nKQ0KPC9jZW50ZXI+ICANCiAgDQogIA0KDQpJbiBgc2ZgIHNwYXRpYWwgam9pbnMgYXJlIGhhbmRsZWQgdXNpbmcgdGhlIGBzdF9qb2luKClgIGZ1bmN0aW9uIHdpdGggYXJndW1lbnRzOg0KDQoqIHggLSBgc2ZgIG9iamVjdCB0byB3aGljaCB3ZSBhcmUgam9pbmluZyBkYXRhIChMSFMgaW4gU1FMKQ0KKiB5IC0gYHNmYCBvYmplY3Qgd2hvc2UgdmFyaWFibGVzIGFyZSBiZWluZyBqb2luZWQgKFJIUyBpbiBTUUwpDQoNCldlIHdpbGwgYmUgam9pbmluZyB0aGUgTWlkZGxlIFN1cGVyIE91dHB1dCBBcmVhcyB0byBMRkIgbG9jYXRpb25zLCB3aGljaCB3aWxsIHRoZW4gYWxsb3cgdXMgdG8gZ3JvdXAgYW5kIHBsb3QgZGF0YSBhdCBNU09BIGxldmVsLg0KDQojIyMgRXhlcmNpc2UgLSBzcGF0aWFsIGpvaW5zDQoNCiogUmVhZCBpbiBgZGF0YS9zaHAvTVNPQV8yMDExX2xvbmRvbi9tc29hXzIwMTFfZXdfYmdjLnNocGAgYXMgYG1zb2FfbG9uZG9uYCAtIHVzZSBgc3RfcmVhZCgpYA0KKiBDaGVjayBpZiBgbXNvYV9sb25kb25gJ3MgQ1JTIGFuZCB0aGF0IG9mIGBsZmJfc2ZgIG1hdGNoLiBUcmFuc2Zvcm0gYG1zb2FfbG9uZG9uYCBpZiBuZWNlc3NhcnkuDQoqIENyZWF0ZSBhIG5ldyBvYmplY3QgY2FsbGVkIGBsZmJfbXNvYV9zZmAgYnkgcnVubmluZyBgc3Rfam9pbigpYCBiZXR3ZWVuIGBsZmJfc2ZgIGFuZCBgbXNvYV9sb25kb25gDQoqIEluc3BlY3QgeW91ciBuZXcgb2JqZWN0IHVzaW5nIGBoZWFkKClgIG9yIGBnbGltcHNlKClgIHRvIHNlZSB3aGF0IGNvbHVtbnMgaGF2ZSBiZWVuIGFkZGVkLg0KDQpgYGB7cn0NCm1zb2FfbG9uZG9uIDwtIHN0X3JlYWQoImRhdGEvc2hwL01TT0FfMjAxMV9sb25kb24vbXNvYV8yMDExX2V3X2JnYy5zaHAiKQ0KDQpzdF9jcnMobXNvYV9sb25kb24pDQoNCnN0X2NycyhsZmJfc2YpDQoNCm1zb2FfbG9uZG9uIDwtIHN0X3RyYW5zZm9ybShtc29hX2xvbmRvbiwgY3JzID0gMjc3MDApDQoNCmxmYl9tc29hX3NmIDwtIHN0X2pvaW4obGZiX3NmLCBtc29hX2xvbmRvbikNCg0KaGVhZChsZmJfbXNvYV9zZikNCmBgYA0KDQoNCkJlZm9yZSB3ZSBwcm9jZWVkIGxldCdzIGNoZWNrIGlmIGFsbCBwb2ludHMgd2VyZSBzdWNjZXNmdWxseSBqb2luZWQuIElmIHRoYXQncyBub3QgdGhlIGNhc2UsIHNvbWUgb2JzZXJ2YXRpb25zIHdpbGwgYmUgYE5BYC4gDQoNCiMjIyBFeGVyY2lzZSAtIHJlbW92ZSBOQSBqb2lucw0KDQoqIFVzZSBgZmlsdGVyKClgIGFuZCBgaXMubmEoKWAgb24gdGhlIGBtc29hMTFjZGAgdmFyaWFibGUgb2YgYGxmYl9tc29hX3NmYCB0byBjaGVjayBpZiBhbnkgcG9pbnRzIGRpZCBub3Qgam9pbiBjb3JyZWN0bHkuIA0KKiBTYXZlIHRob3NlIHRvIGEgbmV3IG9iamVjdCBjYWxsZWQgYGxmYl9tc29hX3NmX25hYCANCiogQ3JlYXRlIGFuIGludGVyYWN0aXZlIG1hcCBvZiB0aGUgcG9pbnRzIGFuZCBzZWUgd2h5IHRoZXkgZGlkIG5vdCBqb2luIGNvcnJlY3RseS4NCiogUmVtb3ZlIGFsbCBgTkFgIG9ic2VydmF0aW9ucyBmcm9tIGBsZmJfbXNvYV9zZmBhbmQgb3ZlcndyaXRlIGl0IC0gdXNlIGAhaXMubmFgIHRvIGZpbmQgdGhlIGNvcnJlY3Qgc3Vic2V0Lg0KDQpTb2x1dGlvbg0KYGBge3J9DQp0bWFwX21vZGUoInZpZXciKQ0KbGZiX3NmX25hIDwtIGZpbHRlcihsZmJfbXNvYV9zZiwgaXMubmEobXNvYTExY2QpKQ0KcXRtKGxmYl9zZl9uYSkNCg0KbGZiX21zb2Ffc2YgPC0gZmlsdGVyKGxmYl9tc29hX3NmLCAhaXMubmEobXNvYTExY2QpKQ0KYGBgDQoNCg0KTm93IHRoYXQgYG1zb2ExMWNkYCBpcyBhdHRhY2hlZCB0byBvdXIgb2JzZXJ2YXRpb25zIHdlIGNhbiBjcmVhdGUgc29tZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIGVhY2ggTVNPQS4gQXMgbWVudGlvbmVkIGJlZm9yZSwgd2UgY2FuIHVzZSBzdGFuZGFyZCBgdGlkeXZlcnNlYCBmdW5jdGlvbnMgb24gYHNmYCBvYmplY3RzLiBIZXJlLCB3ZSB3aWxsIHVzZSBgZHBseXJgIHRvIGNhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIGluY2lkZW50cyBhbmQgdGhlaXIgY29zdCwgYW5kIHRoZW4gdXNlIGEgbm9uIHNwYXRpYWwgam9pbiB0byBhdHRhY2ggdGhvc2UgcmVzdWx0cyB0byBNU09BIGJvdW5kYXJpZXMuIEF0IHRoaXMgc3RhZ2Ugd2Ugbm8gbG9uZ2VyIG5lZWQgdGhlIGdlb21ldHJ5IGNvbHVtbiBmb3IgZWFjaCBMRkIgaW5jaWRlbnQgYXMgYSkgd2UncmUgbm90IHBlcmZvcm1pbmcgYW55IHNwYXRpYWwgb3BlcmF0aW9ucyBvbiBvdXIgcG9pbnRzLCBhbmQgYikgdGhlIGdlb21ldHJ5IGNvbHVtbiBjYW4gc2xvdyBkb3duL2ludGVycnVwdCB0aGUgYGRwbHlyOjpncm91cF9ieWAgZnVuY3Rpb24gd2hpY2ggd2Ugd2lsbCBiZSB1c2luZy4gVG8gcmVtb3ZlIHRoZSBnZW9tZXRyeSBjb2x1bW4gd2UgY2FuIHVzZSB0aGUgYHN0X2Ryb3BfZ2VvbWV0cnkoKWAgZnVuY3Rpb24gZGlyZWN0bHkgaW4gdGhlIGRwbHlyIHBpcGUuIA0KDQojIyMgRXhlcmNpc2UgLSBNU09BIHN1bW1hcnkgc3RhdGlzdGljcw0KDQoqIFRoZSBzdGVwIHJlcXVpcmVzIHlvdSB0byBiZSBmYW1pbGlhciB3aXRoIGBkcGx5cmAncyBtb3JlIGFkdmFuY2VkIGZ1bmN0aW9ucy4gSWYgeW91IGFyZSBzdHJ1Z2dsaW5nIHdpdGggdGhpcyBzdGVwIGxvYWQgYGRhdGEvZ3BrZy9tc29hX2xmYi5ncGtnYCBhcyBgbXNvYV9sZmJgIHVzaW5nIGBzdF9yZWFkYC4NCiogVXNlIGBzdF9kcm9wX2dlb21ldHJ5KClgIG9uIGBsZmJfbXNvYV9zZmAgdG8gcmVtb3ZlIGdlb21ldHJ5IGRhdGEuIA0KKiBDcmVhdGUgc3VtbWFyeSBzdGF0aXN0aWNzIHBlciBNU09BIC0gc3VtIG9mIGNvc3RfZ2JwIGFzIHRvdGFsX2Nvc3QgKHVzZSBgbmEucm0gPSBUUlVFYCksIGFuZCB0aGUgdG90YWwgbnVtYmVyIG9mIGluY2lkZW50cyBhcyBuX2Nhc2VzLiBZb3Ugd2lsbCBuZWVkIHRvIHVzZSBgZ3JvdXBfYnkoKWAgYW5kIGBzdW1tYXJpc2UoKWANCiogQ3JlYXRlIGEgbmV3IGNvbHVtbiBjYWxsZWQgYGNvc3RfcGVyX2luY2lkZW50YCB1c2luZyBgbXV0YXRlYCAtIGB0b3RhbF9jb3N0YCBkaXZpZGVkIGJ5IGBuX2Nhc2VzYC4NCiogSm9pbiBgbGZiX21zb2Ffc3RhdHNgIHRvIGBtc29hX2xvbmRvbmAsIHVzaW5nIGBsZWZ0X2pvaW4oKWAgYW5kIGNyZWF0ZSBhIG5ldyBvYmplY3QgYG1zb2FfbGZiYA0KDQpgYGB7ciwgbWVzc2FnZT1GQUxTRX0NCmxmYl9tc29hX3N0YXRzIDwtIGxmYl9tc29hX3NmICU+JSANCiAgICAgICAgICAgICAgICAgIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUgDQogICAgICAgICAgICAgICAgICBncm91cF9ieShtc29hMTFjZCkgJT4lIA0KICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKHRvdGFsX2Nvc3QgPSBzdW0oY29zdF9nYnAsIG5hLnJtPVRSVUUpLCBuX2Nhc2VzID0gbigpKSAlPiUgDQogICAgICAgICAgICAgICAgICBtdXRhdGUoY29zdF9wZXJfaW5jaWRlbnQgPSB0b3RhbF9jb3N0L25fY2FzZXMpDQogICAgICAgICAgICAgICAgICAgICAgICAgDQoNCm1zb2FfbGZiIDwtIGxlZnRfam9pbihtc29hX2xvbmRvbiwgbGZiX21zb2Ffc3RhdHMpDQptc29hX2xmYg0KYGBgDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KbXNvYV9sZmIgPC0gc3RfcmVhZCgiZGF0YS9ncGtnL21zb2FfbGZiLmdwa2ciKSANCg0KYGBgDQoNCkF0IHRoaXMgc3RhZ2UgaXQgaXMgYSBnb29kIGlkZWEgdG8gc2F2ZSBvdXIgZGF0YS4gV2UgY2FuIGRvIHRoaXMgdXNpbmcgdGhlIGBzdF93cml0ZSgpYCBmdW5jdGlvbi4gSXQgbmVlZHMgYW4gYHNmYCBvYmplY3QgYW5kIHRoZSBwYXRoIGFuZCBuYW1lIG9mIHRoZSBvdXRwdXQuDQoNCiMjIyBFeGVyY2lzZSAtIHNhdmUgZGF0YSB0byBncGtnDQoNCiogQ29weSBhbmQgZXhlY3V0ZSB0aGUgZm9sbG93aW5nIGNvZGUgdG8gc2F2ZSB5b3VyIGRhdGE6IGBzdF93cml0ZShtc29hX2xmYiwib3V0cHV0L21zb2FfbGZiLmdwa2cpYA0KDQoNCiMgTWFraW5nIGJldHRlciBtYXBzDQoNCk5vdyB0aGF0IHdlIGhhdmUgcHJvY2Vzc2VkIG91ciBkYXRhIHdlIGNhbiBzdGFydCBtYXBwaW5nIGl0LiBTbyBmYXIgd2UgaGF2ZSBvbmx5IHVzZWQgdGhlIGBxdG0oKWAgZnVuY3Rpb24gZnJvbSB0aGUgYHRtYXBgIHBhY2thZ2UuIFRoaXMgY3JlYXRlcyBhIGRlZmF1bHQgbWFwIGFuZCBpcyBncmVhdCB3aGVuIGFsbCB3ZSB3YW50IHRvIGRvIGlzIHF1aWNrbHkgdmlzdWFsaXNlIG91ciBkYXRhLiBUaGUgZnVsbCByYW5nZSBvZiBgdG1hcGAgZnVuY3Rpb25zIGdpdmVzIHVzIGNvbnRyb2wgb3ZlciBhbGwgZWxlbWVudHMgb2YgdGhlIGZpbmFsIHBsb3QgYW5kIGFsbG93cyB1cyB0byBjcmVhdGUgaGlnaCBxdWFsaXR5IG1hcHMuDQoNCmBgYHtyLCBmaWcuYWxpZ249J2NlbnRlcicsIG1lc3NhZ2U9RkFMU0V9DQp0bWFwX21vZGUoInBsb3QiKQ0KDQp0bV9zaGFwZShtc29hX2xmYikgKyANCiAgdG1fcG9seWdvbnMoY29sID0gInRvdGFsX2Nvc3QiLCBib3JkZXIuY29sID0gIiM0YTQ5NDkiLCBsd2QgPSAwLjA1LCB0aXRsZSA9ICJUb3RhbCBjb3N0ICjCoykiLCBwYWxldHRlID0gIkJsdWVzIiwgY29udHJhc3QgPSAxLCBsZWdlbmQuaGlzdCA9IFRSVUUsDQogICAgICAgIGxhYmVscyA9IGMoIjAgLSAyLDAwMCIsICI+MiwwMDAgLSA0LDAwMCIsICI+NCwwMDAgLSA2LDAwMCIsICI+NiwwMDAgLSA4LDAwMCIsICI+OCwwMDAgLSAxMCwwMDAiLCANCiAgICAgICAgICAgICAgICAgICAiPjEwLDAwMCAtIDEyLDAwMCIsICI+MTIsMDAwIC0gMTQsMDAwIikpICsNCiAgdG1fc2NhbGVfYmFyKHBvc2l0aW9uID0gYygwLDApLCB0ZXh0LnNpemUgPSAwLjcpICsNCiAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAiQ29zdCBvZiBhbmltYWwgcmVsYXRlZCBpbmNpZGVudHMgcGVyIE1TT0EsIGJldHdlZW4gMjAwOSBhbmQgMjAyMCIsICBtYWluLnRpdGxlLnBvc2l0aW9uID0gIGMoMCwwKSwgbWFpbi50aXRsZS5zaXplID0gMSwgbWFpbi50aXRsZS5mb250ZmFjZSA9ICJib2xkIiwgZnJhbWUgPSBGQUxTRSwgbGVnZW5kLnBvc2l0aW9uICA9IGMoMC4wOCwwLjE4KSwNCiAgICAgICAgICAgIGlubmVyLm1hcmdpbnMgPSBjKDAuMSwwLjA1LDAuMSwwLjAyKSwgbGVnZW5kLm91dHNpZGUgPSBUUlVFLCBsZWdlbmQudGl0bGUuc2l6ZSAgPSAxLCBsZWdlbmQudGV4dC5zaXplID0gIDAuNywgdGl0bGUuc25hcC50by5sZWdlbmQgPSBGQUxTRSkgKw0KICB0bV9zaGFwZShsb25kb25fYm91bmRhcnkpICsgdG1fYm9yZGVycyhjb2wgPSAiYmxhY2siLCBsd2QgPSAwLjI1KQ0KYGBgDQoNCg0KYHRtYXBgIGZvbGxvd3Mgc2ltaWxhciBwcmluY2lwbGVzIHRvIGBnZ3Bsb3QyYCwgd2hlcmUgd2UgZmlyc3Qgc3BlY2lmeSB0aGUgZGF0YSBzb3VyY2UgLSBgdG1fc2hhcGVgLCB0aGVuIHRoZSBhZXN0aGV0aWNzIG9mIHRoZSBwbG90IC0gYHRtX3BvbHlnb25zYCwgYHRtX2RvdHNgLCBldGMuLCBhbmQgdGhlbiB3ZSBtYWtlIGFueSBmaW5hbHkgYWRqdXN0bWVudHMgLSBgdG1fbGF5b3V0YC4gQWxsIGZ1bmN0aW9ucyBuZWVkIHRvIGJlIGNvbm5lY3RlZCB1c2luZyB0aGUgYCtgIHN5bWJvbC4NCg0KKiBgdG1fc2hhcGUoKWAgLSBgc2ZgIG9iamVjdCB3aGljaCB5b3Ugd2FudCB0byBwbG90DQoqIGB0bV9maWxsKClgLCBgdG1fYm9yZGVycygpYCwgYHRtX3BvbHlnb25zKClgLCBgdG1fZG90cygpYCAtIHR5cGVzIG9mIG91dHB1dA0KKiBgdG1fbGF5b3V0KClgIC0gY29udHJvbHMgbGF5b3V0IG9mIHRoZSBtYXAsIHRpdGxlcywgbGFiZWxzLCBldGMuDQoNCmB0bWFwYCBzeW50YXg6IGB0bV9zaGFwZShzZl9vYmplY3QpICsgdG1fYm9yZGVycyhjb2wgPSBlaXRoZXIgImNvbG91ciIgb3IgbmFtZSBvZiBjb2x1bW4gd2hpY2ggd2Ugd2FudCB0byBwbG90KSArIHRtX2xheW91dChtYWluLnRpdGxlID0gInRpdGxlIG9mIHlvdXIgbWFwIilgDQoNCiMjIyBHdWlkZWQgZXhlcmNpc2UgLSBtYXBwaW5nDQoNClN0YXJ0IGJ5IHNwZWNpZnlpbmcgd2hpY2ggYHNmYCBvYmplY3QgaXMgYmVpbmcgbWFwcGVkIGluIGB0bV9zaGFwZSgpYCBhbmQgd2hhdCBjb2x1bW4gaG9sZHMgdGhlIHZhbHVlcyB0byBiZSB2aXN1YWxpc2VkLiBXZSB3aWxsIGFsc28gY2hhbmdlIHRoZSBsZWdlbmQncyB0aXRsZS4NCmBgYHtyfQ0KdG1fc2hhcGUobXNvYV9sZmIpICsgDQogIHRtX3BvbHlnb25zKGNvbCA9ICJjb3N0X3Blcl9pbmNpZGVudCIsIHRpdGxlID0gIkNvc3QgcGVyIEluY2lkZW50ICjCoykiKQ0KYGBgDQoNCk5vdyBsZXQncyBhZGQgYGxvbmRvbl9ib3VuZGFyeWAgdG8gaGF2ZSBhIHRoaWNrZXIgbGluZSBhcm91bmQgTG9uZG9uLg0KYGBge3J9DQp0bV9zaGFwZShtc29hX2xmYikgKyANCiAgdG1fcG9seWdvbnMoY29sID0gImNvc3RfcGVyX2luY2lkZW50IiwgdGl0bGUgPSAiQ29zdCBwZXIgSW5jaWRlbnQgKMKjKSIpICsgDQogIHRtX3NoYXBlKGxvbmRvbl9ib3VuZGFyeSkgKyB0bV9ib3JkZXJzKGNvbCA9ICJibGFjayIpDQpgYGANCg0KTmV4dCB3ZSB3aWxsIGFkZCBhIHNjYWxlIGJhciBhbmQgcG9zaXRpb24gaXQgaW4gdGhlIGJvdHRvbSBsZWZ0IGNvcm5lci4NCmBgYHtyfQ0KdG1fc2hhcGUobXNvYV9sZmIpICsgDQogIHRtX3BvbHlnb25zKGNvbCA9ICJjb3N0X3Blcl9pbmNpZGVudCIsIHRpdGxlID0gIkNvc3QgcGVyIEluY2lkZW50ICjCoykiKSArIA0KICB0bV9zaGFwZShsb25kb25fYm91bmRhcnkpICsgdG1fYm9yZGVycyhjb2wgPSAiYmxhY2siKSArDQogIHRtX3NjYWxlX2Jhcihwb3NpdGlvbiA9IGMoMCwwKSkNCmBgYA0KDQpXZSBjYW4gbm93IHJlbW92ZSB0aGUgYmxhY2sgZnJhbWUgZnJvbSB0aGUgbWFwIGFuZCBhZGQgYSB0aXRsZSB0byBvdXIgbWFwLg0KYGBge3J9DQp0bV9zaGFwZShtc29hX2xmYikgKyANCiAgdG1fcG9seWdvbnMoY29sID0gImNvc3RfcGVyX2luY2lkZW50IiwgdGl0bGUgPSAiQ29zdCBwZXIgSW5jaWRlbnQgKMKjKSIpICsgDQogIHRtX3NoYXBlKGxvbmRvbl9ib3VuZGFyeSkgKyB0bV9ib3JkZXJzKGNvbCA9ICJibGFjayIpICsNCiAgdG1fc2NhbGVfYmFyKHBvc2l0aW9uID0gYygwLDApKSArDQogICB0bV9sYXlvdXQodGl0bGUgPSAiQXZlcmFnZSBjb3N0IG9mIGFuaW1hbCByZWxhdGVkIGluY2lkZW50cyBiZXR3ZWVuIDIwMDkgYW5kIDIwMjAiLCAgDQogICAgICAgICAgICBmcmFtZSA9IEZBTFNFKQ0KYGBgDQoNCkFsbCBvZiB0aGUgbWFwIGVsZW1lbnRzIGFyZSBub3cgdmlzaWJsZSBidXQgdGhleSdyZSBub3QgaW4gdGhlIHJpZ2h0IHBsYWNlLiBXZSBjYW4gc29sdmUgdGhpcyBieSBpbmNyZWFzaW5nIHRoZSBtYXJnaW5zIGFyb3VuZCBvdXIgbWFwLiBUaGlzIHdpbGwgYWxsb3cgdGhlIHRpdGxlIGFuZCB0aGUgbGVnZW5kIHRvIG1vdmUgb3V0d2FyZHMuDQoNCmBgYHtyfQ0KdG1fc2hhcGUobXNvYV9sZmIpICsgDQogIHRtX3BvbHlnb25zKGNvbCA9ICJjb3N0X3Blcl9pbmNpZGVudCIsIHRpdGxlID0gIkNvc3QgcGVyIEluY2lkZW50ICjCoykiKSArIA0KICB0bV9zaGFwZShsb25kb25fYm91bmRhcnkpICsgdG1fYm9yZGVycyhjb2wgPSAiYmxhY2siKSArDQogIHRtX3NjYWxlX2Jhcihwb3NpdGlvbiA9IGMoMCwwKSkgKw0KICAgdG1fbGF5b3V0KHRpdGxlID0gIkF2ZXJhZ2UgY29zdCBvZiBhbmltYWwgcmVsYXRlZCBpbmNpZGVudHMgYmV0d2VlbiAyMDA5IGFuZCAyMDIwIiwgIA0KICAgICAgICAgICAgZnJhbWUgPSBGQUxTRSwgaW5uZXIubWFyZ2lucyA9IGMoMC4xLDAuMSwwLjEsMC4xNSkpDQoNCmBgYA0KDQpXZSBjYW4gYWxzbyBtYW51YWxseSBjaGFuZ2UgdGhlIGxlZ2VuZCBsYWJlbHMgdG8gZW5zdXJlIHRoZXJlIGFyZSBubyBvdmVybGFwcGluZyB2YWx1ZXMuDQpgYGB7cn0NCnRtX3NoYXBlKG1zb2FfbGZiKSArIA0KICB0bV9wb2x5Z29ucyhjb2wgPSAiY29zdF9wZXJfaW5jaWRlbnQiLCB0aXRsZSA9ICJDb3N0IHBlciBJbmNpZGVudCAowqMpIiwNCiAgICAgICAgICAgICAgbGFiZWxzID0gYygiMCAtIDIwMCIsICI+MjAwIC0gNDAwIiwgIj40MDAgLSA2MDAiLCAiPjYwMCAtIDgwMCIsICI+ODAwIC0gMSwwMDAiLCANCiAgICAgICAgICAgICAgICAgICAiPjEsMDAwIC0gMSwyMDAiKSkgKyANCiAgdG1fc2hhcGUobG9uZG9uX2JvdW5kYXJ5KSArIHRtX2JvcmRlcnMoY29sID0gImJsYWNrIikgKw0KICB0bV9zY2FsZV9iYXIocG9zaXRpb24gPSBjKDAsMCkpICsNCiAgIHRtX2xheW91dCh0aXRsZSA9ICJBdmVyYWdlIGNvc3Qgb2YgYW5pbWFsIHJlbGF0ZWQgaW5jaWRlbnRzIGJldHdlZW4gMjAwOSBhbmQgMjAyMCIsICANCiAgICAgICAgICAgIGZyYW1lID0gRkFMU0UsIGlubmVyLm1hcmdpbnMgPSBjKDAuMSwwLjEsMC4xLDAuMTUpKQ0KYGBgDQoNCg0KRmluYWxseSBsZXQncyBjaGFuZ2UgdGhlIGNvbG91ciBvZiBvdXIgbWFwIGFuZCBpbmNyZWFzZSB0aGUgY29udHJhc3QuIENob29zZSBhIGNvbG91ciBmcm9tIFtSIENvbG91cnNdKGh0dHBzOi8vd3d3LnItZ3JhcGgtZ2FsbGVyeS5jb20vMzgtcmNvbG9yYnJld2Vycy1wYWxldHRlc19maWxlcy9maWd1cmUtaHRtbC90aGVjb2RlLTEucG5nKS4NCg0KYGBge3J9DQp0bV9zaGFwZShtc29hX2xmYikgKyANCiAgdG1fcG9seWdvbnMoY29sID0gImNvc3RfcGVyX2luY2lkZW50IiwgdGl0bGUgPSAiQ29zdCBwZXIgSW5jaWRlbnQgKMKjKSIsDQogICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAgLSAyMDAiLCAiPjIwMCAtIDQwMCIsICI+NDAwIC0gNjAwIiwgIj42MDAgLSA4MDAiLCAiPjgwMCAtIDEsMDAwIiwgDQogICAgICAgICAgICAgICAgICAgIj4xLDAwMCAtIDEsMjAwIiksIHBhbGV0dGUgPSAiQmx1ZXMiLCBjb250cmFzdCA9IDEpICsgDQogIHRtX3NoYXBlKGxvbmRvbl9ib3VuZGFyeSkgKyB0bV9ib3JkZXJzKGNvbCA9ICJibGFjayIpICsNCiAgdG1fc2NhbGVfYmFyKHBvc2l0aW9uID0gYygwLDApKSArDQogICB0bV9sYXlvdXQodGl0bGUgPSAiQXZlcmFnZSBjb3N0IG9mIGFuaW1hbCByZWxhdGVkIGluY2lkZW50cyBiZXR3ZWVuIDIwMDkgYW5kIDIwMjAiLCAgDQogICAgICAgICAgICBmcmFtZSA9IEZBTFNFLCBpbm5lci5tYXJnaW5zID0gYygwLjEsMC4xLDAuMSwwLjE1KSkNCmBgYA0KDQoNCg0KRmluYWxseSwgc2F2ZSB5b3VyIG1hcCBhcyBhbiBSIG9iamVjdCBhbmQgZXhwb3J0IGl0Lg0KDQpgYGB7cn0NCmF2ZXJhZ2VfY29zdCA8LSB0bV9zaGFwZShtc29hX2xmYikgKyANCiAgdG1fcG9seWdvbnMoY29sID0gImNvc3RfcGVyX2luY2lkZW50IiwgdGl0bGUgPSAiQ29zdCBwZXIgSW5jaWRlbnQgKMKjKSIsIHBhbGV0dGUgPSAiQmx1ZXMiLCBjb250cmFzdCA9IDEpICsgDQogIHRtX3NoYXBlKGxvbmRvbl9ib3VuZGFyeSkgKyB0bV9ib3JkZXJzKGNvbCA9ICJibGFjayIpICsNCiAgdG1fc2NhbGVfYmFyKHBvc2l0aW9uID0gYygwLDApKSArDQogICB0bV9sYXlvdXQodGl0bGUgPSAiQXZlcmFnZSBjb3N0IG9mIGFuaW1hbCByZWxhdGVkIGluY2lkZW50cyBiZXR3ZWVuIDIwMDkgYW5kIDIwMjAiLCAgDQogICAgICAgICAgICBmcmFtZSA9IEZBTFNFLCBpbm5lci5tYXJnaW5zID0gYygwLjEsMC4xLDAuMSwwLjE1KSkNCmBgYA0KYGBge3IsIGV2YWw9RkFMU0V9DQp0bWFwX3NhdmUoYXZlcmFnZV9jb3N0LCAib3V0cHV0L21hcHMvYXZlcmFnZV9jb3N0X21zb2EucG5nIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA1KQ0KYGBgDQoNCllvdSBjYW4gYWxzbyB2aWV3IHlvdXIgY2hvcm9wbGV0aCBhcyBhbiBpbnRlcmFjdGl2ZSBtYXAuIEl0IGhlbHBzIHRvIGFkZCBhbiBgYWxwaGFgIGFyZ3VtZW50IHRvIGNoYW5nZSB5b3VyIG1hcCdzIHRyYW5zcGFyZW5jeS4NCg0KYGBge3J9DQp0bWFwX21vZGUoInZpZXciKQ0KdG1fc2hhcGUobXNvYV9sZmIpICsgDQogIHRtX3BvbHlnb25zKGNvbCA9ICJjb3N0X3Blcl9pbmNpZGVudCIsIHRpdGxlID0gIkNvc3QgcGVyIEluY2lkZW50ICjCoykiLCBwYWxldHRlID0gIkJsdWVzIiwgY29udHJhc3QgPSAxLCBhbHBoYSA9IDAuNSkgKyANCiAgdG1fc2hhcGUobG9uZG9uX2JvdW5kYXJ5KSArIHRtX2JvcmRlcnMoY29sID0gImJsYWNrIikgDQpgYGANCg0KIyBSZWNvbW1lbmRlZCByZXNvdXJjZXMNCg0KW0dlb2NvbXB1dGF0aW9uIHdpdGggUl0oaHR0cHM6Ly9nZW9jb21wci5yb2JpbmxvdmVsYWNlLm5ldC9pbmRleC5odG1sKSAgDQoNCltTaW1wbGUgRmVhdHVyZXMgZm9yIFJdKGh0dHBzOi8vci1zcGF0aWFsLmdpdGh1Yi5pby9zZi9pbmRleC5odG1sKSAgDQoNCltTcGF0aWFsIERhdGEgU2NpZW5jZSB3aXRoIFJdKGh0dHBzOi8vd3d3LnJzcGF0aWFsLm9yZy8pICANCg0KW0NyZWF0aW5nIGRlbW9ncmFwaGljIG1hcHMgaW4gUiB3aXRoIHRtYXAgcGFja2FnZXNdKGh0dHA6Ly93d3cuemV2cm9zcy5jb20vYmxvZy8yMDE4LzEwLzAyL2NyZWF0aW5nLWJlYXV0aWZ1bC1kZW1vZ3JhcGhpYy1tYXBzLWluLXItd2l0aC10aGUtdGlkeWNlbnN1cy1hbmQtdG1hcC1wYWNrYWdlcy8pDQo=